I have a simple class which does some calculations in its own thread and reports the results to the listener.
class Calculator extends Thread {
protected Listener listener;
public void setListener(Listener l) {
listener = l;
}
public void run() {
while (running) {
... do something ...
Listener l = listener;
if (l != null) {
l.onEvent(...);
}
}
}
}
At any time, the user can call setListener(null) if he doesn't want any events for a certain time period. So, in the run() function, I create a copy of the listener, so I can't run into a NullPointerException which might happen if the listener is set to null after the != null condition check succeeded. In my case, I believe this is a correct alternative for synchronizing it.
My question is: should I declare here the listener member variable as volatile? I have been reading a lot about volatile, but all examples seem to target the basic data types (boolean, int, ...), and not Objects. So therefore, I am not sure whether Objects should/could be declared volatile as well. I believe I have to declare it as volatile, so the thread always has the latest version of the member variable, but I am not sure.
Thanks!
Unlike synchronized methods or blocks, it does not make other threads wait while one thread is working on a critical section. Therefore, the volatile keyword does not provide thread safety when non-atomic operations or composite operations are performed on shared variables.
Yes, volatile must be used whenever you want a mutable variable to be accessed by multiple threads. It is not very common usecase because typically you need to perform more than a single atomic operation (e.g. check the variable state before modifying it), in which case you would use a synchronized block instead.
The volatile modifier is used to let the JVM know that a thread accessing the variable must always merge its own private copy of the variable with the master copy in the memory. Accessing a volatile variable synchronizes all the cached copied of the variables in the main memory.
Making a variable volatile does not protect it from race conditions, it just indicates that the object to which the variable is bound may mutate to prevent compiler optimizations, it is not needed in your case because I & j are modified in the same thread (the loop thread).
Yes. To guarantee that the Calculator
thread sees a new value, set by another thread, you would have to make the variable volatile.
However, volatile
is a quite low-level mechanism and seldom used in client code. I suggest you consider using java.util.concurrent.AtomicReference
in this scenario, which makes sure these things works as expected.
If you use this approach, you can no longer guarantee that a listener will not receive an event notification after setListener(null)
returns. Execution could proceed as follows:
Listener l = listener; // listener != null at this point
// setListener(null) executes here
if (l != null) {
l.onEvent(...);
}
If you need to be guaranteed that no events will be posted to a listener after it has been unregistered, then you need to use synchronized blocks. Declaring listener
to be volatile
won't help. The code should instead be:
public synchronized void setListener(Listener l) {
listener = l;
}
public void run() {
while (running) {
... do something ...
synchronized (this) {
if (listener != null) {
listener.onEvent(...);
}
}
}
}
If you want to avoid the expense of synchronized
all the time, you can do this:
if (listener != null) {
synchronized (this) {
if (listener != null) {
listener.onEvent(...);
}
}
}
This runs the slight risk that you will miss an event after setting a non-null listener. Declaring listener
to be volatile
would probably fix that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With