这个多线程代码是否保证始终返回零?

时间:2015-09-09 02:45:35

标签: java concurrency

我正在拿书做模拟测试,我发现了这个问题:

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,但如果你对理论严格,那么就不能保证这一点。

我说错了吗?

1 个答案:

答案 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

这就是你使用ExecutorServiceExecutorCompletionService并且从不手动处理线程管理的原因,因为它非常容易出错。