Reputation: 2610
Let's say I have a JavaBean User that's updated from another thread like this:
public class A {
private final User user;
public A(User user) {
this.user = user;
}
public void aMethod() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
...a long running task..
user.setSomething(something);
}
});
t.start();
t.join();
}
public void anotherMethod() {
GUIHandler.showOnGuiSomehow(user);
}
}
Is this code thread safe? I mean, when the thread that created A instance and called A.aMethod reads user fields, does it see user in the fresh state? How to do it in appropriate thread safe manner?
Note that I can't modify the user class and I don't know if it's thread safe itself.
Upvotes: 5
Views: 1250
Reputation: 40840
Yes, this is safe. See
Java Language Specification (Java 8) Chapter 17.4.4:
The final action in a thread T1 synchronizes-with any action in another thread T2 that detects that T1 has terminated.
T2 may accomplish this by calling T1.isAlive() or T1.join().
Put this together with 17.4.5. Happens-before Order:
Two actions can be ordered by a happens-before relationship. If one action happens-before another, then the first is visible to and ordered before the second. [..] If an action x synchronizes-with a following action y, then we also have hb(x, y).
So after you call t.join();
in your code you will see the updated changes. Since "the thread that created A instance and called A.aMethod" can impossibly read the value after calling aMethod
and before t.join
is called (because it is busy with method aMethod
), this is safe.
Upvotes: 0
Reputation: 65859
Is this code thread safe? ... does it see user in the fresh state?
Not especially - the fact that user
is final in your code makes almost no difference to thread safety other than the fact that it cannot be replaced.
The bit that should change is the instance variable that is set by setSomething
. It should be marked as volatile
.
class User {
// Marked `volatile` to ensure all writes are visible to other threads.
volatile String something;
public void setSomething(String something) {
this.something = something;
}
}
If however (as you suggest) you do not have access to the User
class, you must then perform a synchronization that creates a memory barrier. In its simplest form you could surround your access to the user
with a synchronized
access.
synchronized (user) {
user.setSomething(something);
}
Added :- It turns out (see here) that this can actually be done like this:
volatile int barrier = 0;
...
user.setSomething(something);
// Forces **all** cached variable to be flushed.
barrier += 1;
Upvotes: 4
Reputation: 22191
Concerning threading, final
fields are just guaranteed to be consistent in case of constructor escape, since the JSR-133 about Memory Barrier mechanism:
The values for an object's final fields are set in its constructor. Assuming the object is constructed "correctly", once an object is constructed, the values assigned to the final fields in the constructor will be visible to all other threads without synchronization. In addition, the visible values for any other object or array referenced by those final fields will be at least as up-to-date as the final fields. What does it mean for an object to be properly constructed? It simply means that no reference to the object being constructed is allowed to "escape" during construction. (See Safe Construction Techniques for examples.) In other words, do not place a reference to the object being constructed anywhere where another thread might be able to see it; do not assign it to a static field, do not register it as a listener with any other object, and so on. These tasks should be done after the constructor completes, not in the constructor.
However, nothing ensures automatic thread-safety about any final fields in the remaining object's life (meaning after wrapping class's constructor execution).. Indeed, immutability in Java is a pure misnomer:
Now, in common parlance, immutability means "does not change". Immutability doesn't mean "does not change" in Java. It means "is transitively reachable from a final field, has not changed since the final field was set, and a reference to the object containing the final field did not escape the constructor".
Upvotes: 0
Reputation: 6203
Note that I can't modify the user class and I don't know if it's thread safe itself.
You have to synchronize your access when accessing the User object. You can for example use the User object to synchronize, so just wrap every access on the user object with something like:
synchronized(user) {
// access some method of the user object
}
That assumes that the user object is only accessed in your threads asynchronously. Also keep the synchronized blocks short.
You could also build a threadsafe wrapper around the user object. I would suggest that if you have a lot of different calls, the code gets cleaner and better to read that way.
good luck!
Upvotes: 0
Reputation: 903
final only makes the reference not re-assignable, but if the reference points to a mutable class, you can still alter the state inside that object, which causes thead-unsafe.
Your code is only thread safe if the User class is immutable, I.e. all properties of User cannot be altered outside the object, all references in the class point to other immutable class.
If it is not case, then you have to properly synchronize its methods to make it thread safe.
Upvotes: 0
Reputation: 115378
marking field as final
just means that reference cannot be changed. It means nothing about thread safity of class User
. If methods of this class that access fields are synchronized (or use other synchronization technique) it is thread safe. Otherwise it is not.
Upvotes: 4