I think this should be safe, but only because you're storing the field in a local variable. After this is done, there's no way for the local variable reference to magically change to null, even if another thread is resetting field's value half-way through.
It seems like this will work as long as the reset method is the reset() method listed above. However, if the reset() method instantiates a new Object (like below), couldn't you end up potentially returning something different than you intended?
void reset() {
field = new FieldType();
}
Yes, this is thread safe.
The synchronized block is to prevent multiple threads from unnecessarily calling computeFieldValue(). Since field is volatile, the accesses in reset and getField are all well-ordered.
If the first check is non-null, getField is done; result is returned.
Otherwise, a lock is acquired, excluding any other thread that might set the field to non-null, but permitting any thread to set field to null. If any thread does set field to null, nothing should have changed; that's the condition that got the thread into the synchronized block. If another thread had already acquired the lock after the current thread's check, and set the field to a non-null value, the second check will detect that.
I guess it depends on exactly what you mean by thread-safe.
You could end up with a situation where a first instance is used after a second. That may be okay, or it may not.