Java中读写操作的易变变量

时间:2019-03-18 18:23:29

标签: java multithreading concurrency synchronized volatile

我正在学习Java中的volatile和同步方法,我发现sync用于x++之类的读写修改操作,而volatile用于读写操作。我想问你两个问题。读写操作的外观如何?

对于第二个问题,我有以下代码:

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;
    }

}

我不明白为什么这是一个读写操作,因为将stop变量从false修改为true。所以这不是读-修改-写操作吗?谢谢!

1 个答案:

答案 0 :(得分:2)

语句stop = true;不是“读写”操作,而只是 write 。它根本不会读取变量的旧值。如果stop的先前值为true,则该语句没有引起注意,而没有引起注意。

“读取-修改-写入”操作(也称为“读取-更新-写入”操作)表示读取前一个值,基于该值计算新值并将新值写回到变量的操作。当不使用特殊的原子更新构造时,此操作的问题在于,到执行写入时,可能发生了并发更新,因此该变量被基于过期的先前值的计算值覆盖

对于您的boolean变量,“读取-修改-写入”操作可能看起来像

if(!stop) stop = true;

stop = !stop;

但是对于第一个变体,缺少并发更新不会有太大影响,因为如果变量已经为true,则该语句无效。如果并发执行,第二个可能会错过更新,因此不能反映正确的翻转操作数量,但是对多个状态使用并发boolean更新通常不容易出错。

“读写”操作(即中间没有“修改/更新”)将是读取旧值以供以后使用并写入不基于旧值的新值的操作。喜欢

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

如果不执行原子操作,仍然会丢失更新。因此,这样的操作还需要多个volative变量。例如。 AtomicInteger.getAndSetVarHandle.getAndSet

因此将您的示例扩展到

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();
        }
    }
}

多个线程可能认为他们发送了停止信号。

如果您将代码修复为

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();
        }
    }
}

恰好一个线程将负责发送停止信号。