简体   繁体   中英

Java happens-before relationship

Although there are a lot of posts on SO and elsewhere about the happens-before relationship, I am having a surprisingly hard time finding a definitive answer to my question.

Consider the two Java threads:

initially, flag == false and data == 0

T1

data = 42;
synchronized(m) {
  flag = true;
}

T2

boolean f;
synchronized(m) {
  f = flag;
}
if (f) {
  int x = data;
}

According to the code above, I believe f could either be assigned the value true or false , there is no guaruntee. Is this correct?

Now, if the two synchronized statements were changed to synchronized(flag) , I think the instruction flag = true will always happen before the instruction f = flag , and thus f will always be assigned the value true . Is this correct?

No, synchronized make no guarantees about which Thread gets there first. In only guarantees that more that one Thread cannot access the synchronized block at a time.

Think of a synchronized block as a room with a lock on the door. We have no idea who will get to the door first, but once they do they go in and lock the door.

Other people must wait until the person in the room is ready to leave and unlocks the door. Once that happens everyone races to get in the door again.

It all depends on the order in which you set your Thread s off. But even then there are no guarantees as the JVM may suspend Thread s and Thread s yield to one-another randomly. In the analogy - the guy in the lead to get to the door might trip on a banana peel. No likely, but always possible.

If you want to have guarantees then you need to wait / notify - so that the reading thread checks the flag , and if it's false , suspends itself in a loop.

The writing thread then sets the flag and notifies the lock monitor which wakes the reading thread.

Here is an example using the Lock and Condition api from Java 5. I'm not saying that you have to use that in preference to synchronized and wait / notify - it's just an example I had lying around that I adapted.

The Reader acquires the lock and then checks the ready flag in a loop. If the flag is false it await s on the condition. This atomically releases the lock and suspends the Thread .

The Writer meanwhile acquires the lock and sets the data and the ready flag. It then calls signalAll and releases the lock .

This wakes the Reader which then reads the ready as true and proceeds to the print statement.

The output will always be 42 (never -1 ).

public class App {

    static volatile boolean ready = false;
    static volatile int data = -1;

    private static class Reader implements Runnable {

        private final Lock lock;
        private final Condition condition;

        public Reader(Lock lock, Condition condition) {
            this.lock = lock;
            this.condition = condition;
        }

        @Override
        public void run() {
            lock.lock();
            try {
                while (!ready) {
                    try {
                        condition.await();
                    } catch (InterruptedException ex) {
                        //oh well
                    }
                }
                System.out.println(data);
            } finally {
                lock.unlock();
            }
        }
    }

    private static class Writer implements Runnable {

        private final Lock lock;
        private final Condition condition;

        public Writer(Lock lock, Condition condition) {
            this.lock = lock;
            this.condition = condition;
        }

        @Override
        public void run() {
            lock.lock();
            try {
                data = 42;
                ready = true;
                condition.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        final ExecutorService executorService = Executors.newFixedThreadPool(2);
        final Lock lock = new ReentrantLock();
        final Condition condition = lock.newCondition();
        executorService.execute(new Reader(lock, condition));
        executorService.execute(new Writer(lock, condition));
        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.DAYS);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM