Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java - do I have to declare my shared listener member variable as volatile?

Tags:

java

volatile

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!

like image 738
Japer D. Avatar asked Nov 22 '11 17:11

Japer D.


People also ask

Does declaring a variable as volatile ensures thread safety?

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.

When should I use volatile in Java?

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.

How do you declare a volatile variable in Java?

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.

Does volatile prevent race condition?

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).


2 Answers

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.

like image 150
aioobe Avatar answered Nov 14 '22 21:11

aioobe


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.

like image 20
Ted Hopp Avatar answered Nov 14 '22 21:11

Ted Hopp