我正在学习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。所以这不是读-修改-写操作吗?谢谢!
答案 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.getAndSet
或VarHandle.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();
}
}
}
恰好一个线程将负责发送停止信号。