在以下示例中声明一个同步方法的目的是什么

问题描述:

您可以复制粘贴下面的示例以在本地计算机上运行。我正在学习MultiThreading,并在线查看了这个例子。我很困惑,为什么添加的方法(在类Counter中)如果声明为同步时会有所作为,则可以从中删除同步的关键字,并且不会影响最终结果,因此我只是想知道服务的目的是什么宣布它同步在以下示例中声明一个同步方法的目的是什么

class Counter { 

    long count = 0; 

    public synchronized void add(long value) { 
     this.count += value; 
     System.out.println(count + "-" + Thread.currentThread().getName()); 
    } 
} 

class CounterThread extends Thread { 

    protected Counter counter = null; 

    public CounterThread(Counter counter) { 
     this.counter = counter; 
    } 

    public void run() { 
     for (int i = 0; i < 10; i++) { 
      try { 
       Thread.sleep(100); 
      } catch (InterruptedException ex) { 
       Logger.getLogger(CounterThread.class.getName()).log(Level.SEVERE, null, ex); 
      } 
      counter.add(i); 
     } 
    } 
} 

public class Example { 

    public static void main(String[] args) { 
     Counter counter = new Counter(); 
     Thread threadA = new CounterThread(counter); 
     Thread threadB = new CounterThread(counter); 
     Thread threadC = new CounterThread(counter); 

     threadA.start(); 
     threadB.start(); 
     threadC.start(); 
    } 
} 
+0

如果删除了睡眠,并提高到100000000次迭代左右(所以它不会只是完成瞬间),并在最后打印计数器的值(主线程加入其他线程)之后,你会看到有所不同。 – immibis 2014-12-19 05:23:19

+0

@immibis是的,我做了一个类似的实验,最终的结果是不是一件预期。 – 2014-12-19 05:52:01

​​关键字可防止threadA,threadB和threadC同时执行这些操作。

  1. 读数区域
  2. 附加价值
  3. 写入次数场

如果删除​​关键字,那么你可以想像:理解this.count += value内部实现三个操作是很重要的以下事件序列:

  1. threadA读数字段(0)
  2. threadB读取计数字段(0)
  3. 的ThreadA增加值来计算从#(计数= 0从#1 +值= 1的例子)
  4. threadB增加值的计数(计数= 0 2 +值= 1的例子)
  5. 的ThreadA(从#3)
  6. threadB从#4(!写入计数字段(11)写入计数字段,因此,在#5的写入操作丢失)
+0

啊哈,我明白了。目前去除同步不影响的代码,但我想,如果值更新过程较长再有就是读取从多个线程发生在同一时间的可能性。得到它了。非常感谢bkail。 – 2014-12-19 05:22:22

+1

是的,这是正确的,而墨菲定律说,当该代码是在生产:-)它会发生。 – 2014-12-19 05:24:06

+0

墨菲是该死的权利! – 2014-12-19 05:25:42

声明班级为​​意味着,无论线程想要执行的方法必须首先获取当前对象的锁。该对象只有一个锁,只有获得它的线程才能执行该方法。其他线程必须等待锁定线程完成。完成该方法后,正在执行的线程将释放该锁。

总之。​​关键字可确保在任何给定时间只有一个线程可以执行该方法。

同步对于以下类中很重要:(a)系统是多线程的; (b)该类具有可在该方法内更改的实例变量。多线程问题通常发生在两个或更多线程同时执行相同的方法时。一个线程可以根据其输入更改实例变量,而另一个线程可以看到这些更改。

在此特定示例中,实例变量count可能会出现问题。如果一个线程将它设置为5,则第二个线程可能会出现并将其设置为10.当第一个线程执行System.out.println调用时,它会打印一个值10 - 这是意外的行为。

+0

我看到它可能会失败。谢谢。 – 2014-12-19 05:22:52

使add()方法同步的原因很重要的是,该行:

this.count += value; 

不是原子动作。这真是编译,因为这(或类似):

long c = count; 
c = c + value; 
count = c; 

这应该是很明显线程。