ZZZ
ZZZ

Reputation: 955

Using a boolean to coordinate two threads in Java

I understand the code section below is problematic because the new value of isAlive set in the kill method might not be visible in the thread.

public class MyClass extends Thread {
    private boolean isAlive;
    
    public void run() {
        while(isAlive) {
            ....
        }
    }

    public void kill() {
        isAlive = false;
    }
}

The typical fix is to declare the isAlive variable as volatile.

My question here is that is there any other ways to achieve this without using volatile? Does Java provide other mechanisms to achieve this?

EDIT: Synchronize the method is also not an option.

Upvotes: 1

Views: 487

Answers (4)

Stephen C
Stephen C

Reputation: 719376

There are three options:

  1. Make the shared variable volatile. (This is the simplest way.)

  2. Use synchronized, either in the form of synchronized methods or synchronized blocks. Note that you need to do both reads and writes for the shared variables while holding the (same) mutex.

  3. Use one of the classes in java.util.concurrent that has a "synchronizing effect"1. Or more precisely, one that you can use to get a happens before relationship between the update and subsequent read of the isAlive variable. This will be documented in the respective classes javadocs.

If you don't use one of those options, it is not guaranteed2 that the thread that calls run() will see isAlive variable change from true to false.

If you want to understand the deep technical reasons why this is so, read Chapter 17.4 of the Java Language Specification where it specifies the Java Memory Model. (It will explain what happens before means in this context.)


1 - One of the Lock classes would be an obvious choice.
2 - That is to say ... your code may not work 100% reliably on all platforms. This is the kind of problem where "try it and see" or even extensive testing cannot show conclusively that your code is correct.

Upvotes: 1

pveentjer
pveentjer

Reputation: 11392

There is no good reason to go for a different option than volatile. Volatile is needed to provide the appropriate happens-before edge between writing and reading; otherwise you have a data-race on your hands and as a consequence the write to the flag might never be seen. E.g. the compiler could hoist the read of the variable out of a loop.

There are cheaper alternative that provide more relaxed ordering guarantees compared to the sequential consistency that volatile provides. E.g. acquire/release or opaque (check out the Atomic classes and the VarHandle). But this should only be used in very rare situations where the ordering constraints reduce performance due to limited compiler optimizations and fences on a hardware level.

Long story short: make the variable volatile because it a simple and very fast solution.

Upvotes: 2

Phil Freihofner
Phil Freihofner

Reputation: 7910

As far as I know, polling a boolean in a while loop as a "kill" control is a perfectly reasonable thing to do. Brian Goetz, in "Java Concurrency in Action" has a code example that is very similar to yours, on page 137 (section 7.1 Task Cancellation).

He states that making the boolean volatile gives the pattern greater reliability. He has a good description of the mechanism on page 38.

When a field is declared volatile, the compile and runtime are put on notice that this variable is shared and that operations on it should not be reordered with other memory operations. Volatile variables are not cached in registers or in caches where they are hidden from other processors, so a read of a volatile variable always returns the most recent write by any thread.

I use volatile booleans and loose coupling as my main method of communication that must occur across threads. AFAIK, volatile has a smaller cost than synchronized and is the most recommended pattern for this situation.

Upvotes: 0

chu3la
chu3la

Reputation: 26

The wait/notify mechanism is embedded deep in the heart of the java language, the superclass of classes, has five methods that are the core of the wait/notify mechanism, notify(), notifyAll(), wait(), wait(long), and wait(long, int), all classes in java inherit from Object, in addition, none of these methods can be overridden in a subclass as they are all declared final here is an example that may help you to understand the concept

public class NotifyAndWait {

public List list;
public NofityAndWait() { list = Collections.synchronizedList(new LinkedList  ());
public String removeItem() throws InterruptedException {
 synchronized(list) {
 while(list.isEmpty())
 list.wait();
 }
 String item = list.remove(0);  
 return item;
 }
 public void addItem(String item) {
 synchronized(list) {
 list.add(item);
 //after adding, nofity and waiting all thread that the list has changed
 list.notifyAll();
 }
 }
 public static void main(String..args) throws Exception {
 final NotifyAndWait obj = new NotifyAndWait();
 Runnable runA = new Runnable() {
 public void run() {
 try {
 String item = enf.removeItem();
 catch(Exception e) {} };
 Runnable runB = new Runnable() {
 public void run() { obj.addItem("Hello"); }
 };
 
 Thread t1 = new Thread(runA, "T1");
 t1.start();  
 Thread.sleep(500);
 Thread t2 = new Thread(runB, "T2");
 t2.start();
 Thread.sleep(1000);
}
}

Upvotes: 0

Related Questions