为什么此多线程计数器产生正确的结果?

时间:2019-02-26 18:57:23

标签: java multithreading

我正在学习多线程计数器,并且想知道为什么无论我运行多少次它都会产生正确的结果。

public class MainClass {
    public static void main(String[] args) {
        Counter counter = new Counter();
        for (int i = 0; i < 3; i++) {
            CounterThread thread = new CounterThread(counter);
            thread.start();
        }
    }
}

public class CounterThread extends Thread {
    private Counter counter;
    public CounterThread(Counter counter) {
        this.counter = counter;
    }
    public void run() {
        for (int i = 0; i < 10; i++) {
            this.counter.add();
        }
        this.counter.print();
    }
}

public class Counter {
    private int count = 0;
    public void add() {
        this.count = this.count + 1;
    }
    public void print() {
        System.out.println(this.count);
    }
}

这就是结果

10
20
30

不确定这只是a幸还是期望?我以为结果会是

10
10
10

2 个答案:

答案 0 :(得分:4)

尝试将循环计数从10增加到10000,您可能会发现输出有所不同。

最合乎逻辑的解释是,仅添加10个线程,一个线程在下一个线程开始并在上一个结果之上进行添加之前就无法完成。

答案 1 :(得分:0)

  

我正在学习多线程计数器,并且想知道为什么无论我运行多少次它都会产生正确的结果。

查看@manouti的答案。

即使您共享不同步的同一个Counter对象,也有一些原因导致3个线程与数据同步串行地运行(或看起来像它们在运行)。我必须在8 proc英特尔Linux机器上努力工作,以使其显示出任何交错状态。

  • 当线程启动和结束时,会遇到内存障碍。根据Java内存模型,可以保证执行thread.join()的线程将看到发布给它的线程的结果,但是我怀疑线程完成后会发生中央内存刷新。这意味着,如果线程串行运行(并且循环如此之小,它们 not 很难执行),它们的行为就好像没有并发,因为它们会看到彼此对{{1 }}。

  • 在线程Counter方法的前面放置一个Thread.sleep(100);会导致它不能串行运行。也希望它导致线程缓存run(),而看不到其他已经完成的线程发布的结果。仍然需要帮助。

  • 实例化所有线程后,在循环中启动线程有助于并发性。

  • 引起同步的另一件事是:

    Counter

    System.out.println(this.count); System.out,它是一个同步类。线程每次调用Printstream时,都会将其结果发布到中央内存中。如果您记录了该值然后在以后显示它,则它可能显示出更好的交织。

  • 我真的很怀疑println(...)类中的某些Java compiler inlining是否在某种程度上引起了人工同步。例如,对于Counter方法的前端和结尾处的Thread.sleep(1000)并没有显示10、10、10,我感到非常惊讶。

  • 应注意,在非英特尔架构上,具有不同的内存和/或线程模型,这可能更易于重现。

  • 哦,作为注释,没有什么建议,通常建议实施thread.run()而不是扩展Runnable

因此,以下是我对您的测试程序的调整。

Thread

然后您的主要人员可以执行以下操作:

public class CounterThread extends Thread {
    private Counter counter;
    int result;
    ...
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e1) {
            Thread.currentThread().interrupt();  // good pattern
            return;
        }
        for (int i = 0; i < 10; i++) {
            counter.add();
        }
        result = counter.count;
        // no print here
    }
}

即使这样,我也从未在盒子上看到10,10,10的输出,而我经常看到10,20,30。我最近得到的是12,12,12。

向您显示正确测试线程程序有多困难。相信我,如果这段代码正在生产中并且您正在期待,那么“免费”同步就是您失败的时候。 ;-)