[英]AtomicInteger is not working properly in java
尽管我使用 AtomicBoolean 和 AtomicInteger 我有作家和读者线程,但我可以在阅读器线程中看到重复的值,请帮助我找出我的代码有什么问题。
package automic;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class AutomicTest {
public volatile AtomicBoolean isStopped = new AtomicBoolean(false);
public AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
AutomicTest test = new AutomicTest();
Thread writerThread = new Thread(() ->{
while(!test.isStopped.get()) {
test.count.incrementAndGet();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread readerThread = new Thread(() ->{
while(!test.isStopped.get()) {
System.out.println("Counter :"+test.count.get());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
writerThread.start();
readerThread.start();
Thread.sleep(4000);
test.isStopped.getAndSet(true);
writerThread.join();
readerThread.join();
}
}
Counter :1
Counter :2
Counter :3 // duplicate
Counter :3 // duplicate
Counter :4
Counter :5
Counter :7
Counter :8
Counter :9
Counter :10
Counter :11
Counter :12 // duplicate
Counter :12 // duplicate
Counter :13
Counter :15 // duplicate
Counter :15 // duplicate
Counter :17
Counter :18
Counter :19
Counter :20
Counter :21
Counter :22
Counter :23
Counter :24
Counter :25
Counter :26
Counter :27
Counter :28
Counter :29
Counter :30
Counter :31
Counter :32
Counter :33
Counter :34
Counter :35
Counter :36
Counter :37
Counter :38
Counter :39
Counter :40
从中获得的两大收获是:
Thread.sleep(100)
并不意味着“睡眠 100 毫秒,精确到纳秒”。 它不太精确,取决于内部操作系统时钟的粒度和准确性、本地线程调度以及计算机上运行的其他任务。 即使是睡眠-唤醒周期也需要一些(惊人的高)时间。 因此,您不能使用sleep()
和 atomics 来安排两个线程在完美平衡的滴答声周期中运行。
这发生在您的代码中:
请注意,线程甚至可以翻转,在这种情况下,您会在序列中看到缺少的数字,有时您会看到。 为了平衡线程以在完美的 ABAB 方案中运行,您可以执行以下操作:
public class AutomicTest {
private volatile boolean isStopped = false;
private final CyclicBarrier barrier = new CyclicBarrier(2);
private int count = 0;
public static void main(String[] args) throws InterruptedException {
AutomicTest test = new AutomicTest();
Thread writerThread = new Thread(() -> {
while (!test.isStopped) {
test.count++;
try {
test.barrier.await();
Thread.sleep(100);
} catch (InterruptedException | BrokenBarrierException ignored) {
Thread.currentThread().interrupt();
break;
}
}
});
Thread readerThread = new Thread(() -> {
while (!test.isStopped) {
try {
test.barrier.await();
System.out.println("Counter: " + test.count);
Thread.sleep(100);
} catch (InterruptedException | BrokenBarrierException ignored) {
Thread.currentThread().interrupt();
break;
}
}
});
writerThread.start();
readerThread.start();
Thread.sleep(4000);
test.isStopped = true;
writerThread.join();
readerThread.join();
}
}
这里的关键是一个CyclicBarrier
,它是:
一种同步辅助工具,它允许一组线程相互等待以达到共同的障碍点。 CyclicBarriers 在涉及固定大小的线程组的程序中很有用,这些线程组必须偶尔相互等待。 屏障被称为循环的,因为它可以在等待线程被释放后重新使用。
在这种情况下,屏障被设置为有两个同步方 - 写入器和读取器:
在此方案中, count
数值的可见性由CyclicBarrier
强制执行,因此您甚至不需要AtomicInteger
。 进一步来说:
调用
await()
之前线程中的操作发生在从其他线程中的相应await()
成功返回之后的 [...] 操作。
哦, isStopped
也不需要AtomicBoolean
,一个volatile
就足够了。 但它会以任何一种方式工作。 抱歉,我知道这应该是练习原子的任务,但如果您需要线程相互等待,它们不是一个好工具。
脚注:当您删除sleep()
调用时,上述机制仍然不完全正确。 原因是一旦发布,Reader 会在下一次循环迭代中与 Writer 竞争。 要解决这个问题,Writer 必须等待前一个 Reader 完成,而 Reader 必须等待其 Writer 完成。 这可以通过使用第二个屏障或者可能是我在上面的示例中故意没有使用的Phaser
来实现,因为它更高级,您需要在继续使用 Phasers 之前学习 CyclicBarriers 和 CountDownLatches。 还需要调整关闭机制。 祝你好运!
编辑:我实际上编写了 no- sleep()
双相位器解决方案,发现它更容易阅读(如果您不关心通常应该的长时间运行任务中断!)并且比等效的要快得多CyclicBarrier 解决方案。 所以我们今天都学到了一些东西。 这里是:
public class AutomicTest {
private volatile boolean isStopped = false;
private final Phaser valueWritten = new Phaser(2);
private final Phaser valueRead = new Phaser(2);
private int count = 0;
public static void main(String[] args) throws InterruptedException {
AutomicTest test = new AutomicTest();
Thread writerThread = new Thread(() -> {
while (!test.isStopped) {
// wait for the previous value to be read
test.valueRead.arriveAndAwaitAdvance();
test.count++;
// acknowledge the write
test.valueWritten.arrive();
}
});
Thread readerThread = new Thread(() -> {
while (!test.isStopped) {
// wait for the value to be written
test.valueWritten.arriveAndAwaitAdvance();
System.out.println("Counter: " + test.count);
// acknowledge the read
test.valueRead.arrive();
}
});
writerThread.start();
readerThread.start();
test.valueRead.arrive(); // start the writer
Thread.sleep(4000);
test.isStopped = true;
test.valueRead.forceTermination();
test.valueWritten.forceTermination();
writerThread.join();
readerThread.join();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.