下面的代码片段使用AtomicInteger使用多个线程来计数到1亿。我有 10个Writer线程用于模拟写入争用,还有单个Reader线程用于模拟读取争用。 Writer和Reader也共享 volatile布尔变量作为毒丸。
但是,只有Reader线程可以退出而Writer则不能退出。尽管将变量声明为volatile,但为何Reader线程无法查看更新后的值?
public class AtomicRunnerV2 {
private static final int THREADS = 10;
private static final Integer MAX_COUNT = 100_000_000;
public static void main(String[] args) throws Exception {
AtomicInteger atomicInteger = new AtomicInteger(1);
Boolean data = new Boolean(false);
ExecutorService executorService = Executors.newFixedThreadPool(THREADS + 1);
for (int i = 0; i < THREADS; i++) {
executorService.submit(new Writer(atomicInteger, data));
}
executorService.submit(new Reader(atomicInteger, data));
executorService.shutdown();
}
static class Writer implements Runnable {
private AtomicInteger integer;
private volatile Boolean data;
Writer(final AtomicInteger integer, Boolean data) {
this.integer = integer;
this.data = data;
}
@Override public void run() {
while (!data) {
integer.incrementAndGet();
}
System.out.println("count " + integer.get() + " from WRITER!");
}
}
static class Reader implements Runnable {
private AtomicInteger integer;
private volatile Boolean data;
Reader(final AtomicInteger integer, Boolean data) {
this.integer = integer;
this.data = data;
}
@Override public void run() {
while (!data) {
if (integer.get() >= MAX_COUNT) {
data = true;
}
}
System.out.println("count " + integer.get() + " from READER!");
}
}
}
P.S:如果我将布尔值包装在一个对象中,它将起作用。
答案 0 :(得分:1)
您的volatile boolean data
不是参考,我认为您是在尝试提出建议,而是副本。因此,Reader中的数据将永远不会设置为false。如果您在Reader中使用Writer.data(并将其公开),则此方法应该起作用。
答案 1 :(得分:0)
布尔值是不变的。
data = true;
Writer中的上述行实质上创建了一个新的Boolean实例,从而导致Reader永远不会看到新值,因为在其Boolean引用中没有“更新”任何内容。
通过使用以下自定义类替换代码中所有布尔值出现来进行测试,并且Reader线程能够查看更新后的值。
static class MyBoolean {
private boolean value;
MyBoolean(final boolean value) {
this.value = value;
}
boolean isValue() {
return value;
}
void setValue(final boolean value) {
this.value = value;
}
}