2011-03-27 73 views
4

当我们说我们使用synchronized关键字锁定一个对象时,这是否意味着我们正在获取整个对象的锁或仅锁定了该块中存在的代码?Java同步锁

在下面的例子中listOne.add是同步的,这是否意味着如果另一个线程访问listOne.get它会被阻塞,直到第一个线程离开这个块?如果第二个线程在第一个线程仍处于同步块中时访问同一对象的实例变量的listTwo.getlistTwo.add方法,该怎么办?

List<String> listONe = new ArrayList<String>(); 
List<String> listTwo = new ArrayList<String>(); 

/* ... ... ... */ 

synchronized(this) { 
    listOne.add(something); 
} 

回答

6

给出的方法:

public void a(String s) { 
    synchronized(this) { 
     listOne.add(s); 
    } 
    } 

    public void b(String s) { 
    synchronized(this) { 
     listTwo.add(s); 
    } 
    } 

    public void c(String s) { 
     listOne.add(s); 
    } 

    public void d(String s) { 
     synchronized(listOne) { 
     listOne.add(s); 
     } 
    } 

你不能在同一时间拨打a和b,因为他们被锁定在同一个锁。 但是,您可以同时调用a和c(显然具有多个线程),因为它们未锁定在同一个锁上。这可能会导致listOne出现问题。

您也可以同时调用a和d,因为d在此上下文中与c没有区别。它不使用相同的锁。

重要的是,您始终使用相同的锁锁定listOne,并且不允许在没有锁的情况下访问它。如果listOne和listTwo在某种程度上是相关的,并且有时需要同时/原子地更新,则需要一个锁才能访问它们。否则2个独立的锁可能会更好。

当然,你可能会使用较新的java.util.concurrent类,如果你需要的是一个并发的名单:)

3

如果在同一实例上有一个同步块,其他线程将仅阻塞。因此,列表本身的任何操作都不会阻止。

6

锁定是您在synchronized块包括对象实例。

但保重!该对象是不是内部锁定以供其他线程访问。只有执行相同synchronized(obj)的线程,其中obj在您的示例中为this,但在其他线程中也可以是变量引用,请等待该锁定。

因此,不执行任何同步语句的线程可以访问“锁定”对象的任何和所有变量,您可能会遇到竞争条件。

1

您需要了解该锁是建议性的,而且不是物理强制执行的。例如,如果您决定在哪里使用Object来锁定对某些类字段的访问权限,则必须以实际获取锁定的方式编写代码,然后才能访问这些字段。如果你没有,你仍然可以访问它们,并可能导致死锁或其他线程问题。

此例外是在方法中使用​​关键字,在该方法中,运行时将自动为您获取锁,而无需执行任何特殊操作。

0

Java语言规范defines的​​语句的含义如下:

A​​语句代表正在执行的线程获取互斥锁(第17.1节),执行块并释放锁。当正在执行的线程拥有该锁定时,没有其他线程可以获取该锁定。

SynchronizedStatement:` 
    synchronized (Expression) Block` 

表达式的类型必须是引用类型,否则会发生编译时错误。

通过首先评估表达式来执行同步语句。

如果由于某种原因,表达式的求值突然完成,那么出于同样的原因,同步语句会突然完成。

否则,如果Expression的值为null,则抛出NullPointerException。

否则,让表达式的非空值为V.执行线程锁定与V相关的锁。然后执行块。如果该块的执行正常完成,则该锁被解锁并且同步语句正常完成。如果由于某种原因块的执行突然完成,则锁定被解锁,同步语句突然完成,原因相同。

获取与对象关联的锁本身并不会阻止其他线程访问该对象的字段或调用该对象的未同步方法。其他线程也可以以传统方式使用同步方法或synchronized语句来实现互斥。

也就是说,在你的榜样

synchronized(this) { 
    listOne.add(something); 
} 

synchronized块做治疗由listOne任何特殊方式引用的对象,其他线程可以随心所欲使用它。但是,它确保没有其他线程可以同时为this所指的对象输入同步块。因此,如果所有使用listOne的代码处于同一个对象的同步块中,则最多一个线程可以在任何给定时间使用listOne

还要注意的是,对象被锁定在获取其状态的并发访问没有特殊的保护,所以代码

void increment() { 
    synchronized (this) { 
     this.counter = this.counter + 1; 
    } 
} 

void reset() { 
    this.counter = 0; 
} 

被错误地同步,作为第二个线程可以执行reset而第一线程读取了,但尚未写入,counter,导致重置被覆盖。