【问题标题】:Java Synchronisation with and without static variable有和没有静态变量的Java同步
【发布时间】:2015-07-08 22:10:23
【问题描述】:

我一直在研究java中的同步,并尝试运行以下程序

    public class Example {
        public static void main(String[] args) {
            Counter counterA = new Counter();
            Counter counterB = new Counter();
              Thread  threadA = new CounterThread(counterA);
              Thread  threadB = new CounterThread(counterB);

              threadA.start();
              threadB.start(); 
        }
    }

    class CounterThread extends Thread {
        protected Counter counter = null;

        public CounterThread(Counter counter){
           this.counter = counter;
        }
        public void run() {
        for(int i=0; i<2; i++){
              counter.add(i);
           }
        }
    }

    class Counter {
         long count = 0;
         public synchronized void add(long value){
          this.count += value;
          System.out.println(this.count);
        }
    }

当我运行上述代码时,当我将 Example 类作为 java 应用程序运行或调试 Example 类时,它会给出相同的输出

 0
 1
 0
 1

但是,如果我将计数器类的计数变量的访问修饰符修改为静态,如下所述:

    static long count = 0;

现在如果尝试运行示例类,我得到的输出为

 0
 1
 0
 2

但是当我调试示例类时,我得到的输出为

 0
 1
 1
 2

谁能帮我理解其中的区别。 在此先感谢并道歉,因为我是多线程概念的新手

【问题讨论】:

    标签: java multithreading static


    【解决方案1】:

    每个实例都有自己的实例变量副本,但它们共享静态变量。由于您有两个类实例和两个在这些实例上运行的线程:

            Counter counterA = new Counter();
            Counter counterB = new Counter();
            Thread  threadA = new CounterThread(counterA);
            Thread  threadB = new CounterThread(counterB);
    

    所以效果很明显。当count为非静态时,两个线程不会影响两个不同对象的count。但是当count 是静态的时,两个线程在两个实例共享的同一个变量上工作。

    当您有两个独立的线程在运行时,执行顺序是动态的,具体取决于线程调度程序如何选择要执行的线程。使用调试器不一定会影响它,事实上您可能会在程序正常执行时看到不同的结果。

    【讨论】:

    • 但如果计数是静态的,那么正常执行和调试器执行应该给出相同的结果?
    • @user3403462 静态变量如何影响线程调度程序?没有意义,也没有联系。
    【解决方案2】:

    synchronized 仅保护您免受两个调用者同时在同一实例上输入方法。所以它基本上不会影响你的例子。

    非静态情况:每个线程都在更新自己的Counter.count 实例。您可以得到 0-1-0-1 或 0-0-1-1(不太可能)作为输出。

    静态情况:每个线程通过两个不同 Counter 实例更新相同的Counter.count。您可以得到 0-0-1-2、0-1-1-2 和 0-1-0-2。

    为什么是最后一个选项?您只会影响静态 Coutner.count 实例,而不是静态 Counter 实例。所以两个线程都可以在Counter.add 内部,一个线程可能已经加载了 0 来输出它,但是其他线程然后更新它并输出 1,然后第一个线程才输出它的 0。

    您得到的 0-0-1-2、0-1-1-2 或 0-1-0-2 中的哪一个根本没有定义,并且 NOT 取决于调试和发布模式。然而,由于编译器生成的汇编代码的差异,在调试和发布模式下收到不同的结果是很常见的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-21
      • 2017-02-25
      • 2019-10-23
      • 2020-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-29
      相关资源
      最近更新 更多