我正在拿书做模拟测试,我发现了这个问题:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
答案:
该程序将始终打印0.
但我认为无法保证线程在打印计数器值时完成.
我的意思是,大部分时间它会返回0,但如果你对理论严格,则无法保证这一点.
我对么?
use*_*740 10
有保证.而且没有保证.没有回旋的余地.
在这种情况下,无法保证结果始终为零.这是因为线程没有连接 - 甚至可能永远不会在打印之前实际运行!
例如,其它的排列中,该代码序列可能已执行的是:
counter.decrementAndGet(); // #1
System.out.println(counter); // Main thread
counter.incrementAndGet(); // #2
// (and so on, at some arbitrary point after the print)
Run Code Online (Sandbox Code Playgroud)
避免这种不期望的交错/执行在Java(根据JLS)处理之前发生的关系.
如果线程被连接(带有打印的线程),则会发生之前发生的事情 - 在这种情况下,这意味着线程开始并完成运行 - 结果将保证为零.
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);
}
Run Code Online (Sandbox Code Playgroud)
请参阅以下重复项之一:等到子线程完成:Java
这就是为什么你使用ExecutorService或者ExecutorCompletionService永远不会手动处理线程管理的原因,因为它非常容易出错.