2014-06-30 22 views
3

我看到这个Java的同步教程:在java中的同步保护修改后的类字段吗?

public class SynchronizedCounter { 
    private int c = 0; 

    public synchronized void increment() { 
     c++; 
    } 

    public synchronized void decrement() { 
     c--; 
    } 

    public synchronized int value() { 
     return c; 
    } 
} 

教程说,通过不同的线程后续调用阻塞,就像这样:
答:增量();
B:increment();答:必须等待A完成

......但说两个线程交错如此:
答:增量():加载c - > 0; B:递减():加载c - > 0; A:增量():增量C - > 1; B:递减():递减c - > -1; A:增量():保存C - > 1; B:递减():保存c - > -1;
最后:c == -1;

根据我对本教程的理解,无论如何,所以是保护内存不一致?

由于大众需求:the tutorial

+2

请提供本教程的链接。是否有其他方法,加载和保存,这是类的一部分,但不同步? –

+0

将教程与您的问题进行比较,关于调用问题中各种方法的线程的材料不在教程中,那么来自哪里?你写了一个真正的Java程序来试用它,这些是结果还是什么? –

+0

我刚刚制作了两个线程,分别调用increment()和decrement(),因为它不在本教程中;我想知道发生了什么,因为它不在教程中。 – user2738698

回答

4

上的(非静态)方法synchronized关键字使方法以锁定被封闭的物体上 - 在这种情况下SynchronizedCounter的实例。因此,每个同步方法都会阻止其他同步方法在另一个线程中运行。所以在你的例子中,线程A对increment()的调用将在线程B调用decrement()之前完成。因此你的班级是线程安全的。

+0

你的意思是“封闭”对象吗?如在,该方法属于的对象?因为在synchronized方法中唯一包含的内容是int c ...或c被包装在一个对象中然后被锁定了? – user2738698

0

是的,c被防止内存不一致。

但是,如果通过getter方法将c的值从对象中复制出来,那么该副本将单独生活并变得过时。

SynchronizedCounter上的所有方法都不能同时被调用,因为它们都被声明为同步的。

这样做有两个方面,首先它通过要求任何线程进入监视器(http://en.wikipedia.org/wiki/Monitor_%28synchronization%29)创建一个互斥区域,其次它将内存障碍(http://en.wikipedia.org/wiki/Barrier_%28computer_science%29)置于方法和出口的入口处。

因此,一次只有一个线程可以修改c,无论哪个CPU内核正在执行该方法的哪个线程,它将读取c的最新值,并将其更改为c对于下一个线程可见进入显示器。有关内存障碍的更多信息,Doug Lea在http://gee.cs.oswego.edu/dl/jmm/cookbook.html上写了一篇非常有用的指南。

0

您目前对Java线程的理解尚未完成。在Java中,每个对象都有一个“隐式锁定”,这是对象本身。而“同步”关键字使用这种隐式锁定来让你的代码线程安全。下面的代码有相同的效果:

public class SynchronizedCounter { 
    private int c = 0; 

    public void increment() { 
     synchronized(this){ 
      c++; 
     } 
    } 

    public void decrement() { 
     synchronized(this){ 
      c--; 
     } 
    } 

    public int value() { 
     synchronized(this){ 
      return c; 
     } 
    } 
} 

另外,如果你愿意,你可以声明多个对象服务器一样简单锁定的对象。这又具有相同的效果:

public class SynchronizedCounter { 
    private int c = 0; 
    private Object lockObject; 
    public void increment() { 
     synchronized(lockObject){ 
      c++; 
     } 
    } 

    public void decrement() { 
     synchronized(lockObject){ 
      c--; 
     } 
    } 

    public int value() { 
     synchronized(lockObject){ 
      return c; 
     } 
    } 
} 

同步方法或同步块(有时也称为防护块)之前被执行,正在执行的线程必须首先获得锁。否则,同步方法/块中的代码将不会执行。这就是为什么你的例子总是能够工作的原因,因为所有的方法共享一个锁(隐式的),没有一个方法可以与其他方法同时执行。

对于先进的锁定机制,请参阅Lock interface