我正在拿书做模拟测试,我发现了这个问题:
import java.util.concurrent.atomic.AtomicInteger;
class AtomicVariableTest {
private static AtomicInteger counter = new AtomicInteger(0);
static class Decrementer extends Thread {
public void run() {
counter.decrementAndGet(); // #1
}
}
static class Incrementer extends Thread {
public void run() {
counter.incrementAndGet(); // #2
}
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Incrementer().start();
new Decrementer().start();
}
System.out.println(counter);
}
}
答案:
此程序将始终打印0。
但我认为无法保证线程在打印计数器值时已完成。
我的意思是,大部分时间它会返回0,但如果你对理论严格,那么就不能保证这一点。
我说错了吗?
答案 0 :(得分:10)
有保证。而且没有保证。没有中间立场。
在这种情况下,没有保证结果始终为零。这是因为线程没有连接 - 甚至可能永远不会在打印之前实际运行!
例如,在其他排列中,此代码可以执行的序列是:
counter.decrementAndGet(); // #1
System.out.println(counter); // Main thread
counter.incrementAndGet(); // #2
// (and so on, at some arbitrary point after the print)
避免这种不希望的交错/执行是在Java(根据JLS)处理之前发生的关系。
如果线程 加入(带有print的线程),那么就会发生before-before - 在这种情况下,这意味着线程已启动并且完成了运行 - 结果将保证为零。
public static void main(String[] args) {
final List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 5; i++) {
final new Incrementer i = new Incrementer();
threads.add(i);
i.start();
final new Decrementer d = new Decrementer();
threads.add(d);
d.start();
}
for (final Thread t : threads) { t.join(); }
System.out.println(counter);
}
请参阅其中一个重复项:Wait until child threads completed : Java
这就是你使用ExecutorService
或ExecutorCompletionService
并且从不手动处理线程管理的原因,因为它非常容易出错。