简体   繁体   中英

Volatile variable for read-write operations in Java

I'm learning about volatile and synchronized in Java and I see that synchronized is used for read-modify-write operations like x++ , and volatile is for read-write operations. And I want to ask you 2 questions. How looks a read-write operation?

And for the second question I have this code:

public class StopThread {
    private static volatile boolean stop;

    public static void main(String[] args) throws InterruptedException { 

        new Thread(new Runnable() {

            @Override
            public void run() {
                while (!stop) {
                    System.out.println("In while...");
                }
            }

        }).start();

        TimeUnit.SECONDS.sleep(1); 
        stop = true;
    }

}

And I don't understand why this is a read-write operation because the stop variable will be modified from false to true. So isn't this a read-modify-write operation? Thank you!

The statement stop = true; is not a “read-write” operation but only a write . It doesn't read the variable's old value at all. If stop 's previous value was true , the statement had no effect, without noticing the difference.

A “read-modify-write” operation, also known as “read-update-write” operation implies an operation that reads the previous value, calculates a new value based on it, and writes the new value back to the variable. The problem with this operation, when not using a special atomic update construct, is that by the time, the write is performed, a concurrent update may have happened, so the variable is overwritten with a calculated value which is based on an outdated previous value.

For your boolean variable, “read-modify-write” operations may look like

if(!stop) stop = true;

or

stop = !stop;

but for the first variant, missing a concurrent update would not have much impact, as the statement has no effect if the variable is already true . The second may miss updates if performed concurrently, hence, not reflect the correct number of flip operations, but using concurrent boolean updates for more than a single state transition is error prone in general.

A “read-write” operation, ie without a “modify/update” in between, would be an operation that reads the old value for later use and writes a new value not based on the old value. Like

Type old = variable;
variable = newValue;
// use old here

which still would be subject to lost updates when not done atomically. Thus, such operation also needs more than a volative variable. Eg AtomicInteger.getAndSet or VarHandle.getAndSet .

So expanding your example to

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class StopThread {
    private static volatile boolean stop;

    public static void main(String[] args) throws InterruptedException { 

        new Thread(new Runnable() {
            @Override
            public void run() {
                while(!stop) {
                    System.out.println("In while...");
                }
            }
        }).start();
        for(int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); 
                    boolean old = stop; // broken because
                    stop = true;        // not atomic
                    System.out.println(old? "other thread was faster": "sent stop signal");
                }
            }).start();
        }
    }
}

multiple threads may think that they sent the stop signal.

If you fix the code to

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;

public class StopThread {
    private static final AtomicBoolean stop = new AtomicBoolean();

    public static void main(String[] args) throws InterruptedException { 

        new Thread(new Runnable() {
            @Override
            public void run() {
                while(!stop.get()) {
                    System.out.println("In while...");
                }
            }
        }).start();
        for(int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); 
                    boolean old = stop.getAndSet(true);
                    System.out.println(old? "other thread was faster": "sent stop signal");
                }
            }).start();
        }
    }
}

exactly one thread will take the responsibility of having sent the stop signal.

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