[英]Java Synchronization Lock
当我们说使用synchronized关键字锁定一个对象时,它是否意味着我们正在获取整个对象的锁定或仅获取块中存在的代码?
在以下示例中, listOne.add
是同步的,是否意味着如果另一个线程访问listOne.get
,它将被阻塞,直到第一个线程离开此块为止? 如果第二个线程在第一个线程仍在同步块中时访问同一对象的实例变量上的listTwo.get
或listTwo.add
方法,该怎么办?
List<String> listONe = new ArrayList<String>();
List<String> listTwo = new ArrayList<String>();
/* ... ... ... */
synchronized(this) {
listOne.add(something);
}
锁定在您包含在synchronized块中的对象实例上。
但要小心! 该对象本质上不会被其他线程锁定。 只有执行相同的线程synchronized(obj)
,其中obj在你的例子中是this
但在其他线程中也可以是变量引用,等待该锁定。
因此,不执行任何同步语句的线程可以访问“锁定”对象的任何和所有变量,并且您可能会遇到竞争条件。
鉴于方法:
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类:)
如果在同一实例上有同步块,则其他线程将仅阻止。 因此,列表本身的任何操作都不会阻止。
synchronized(this) {
只会锁定对象this
。 锁定和使用对象listOne
:
synchronized(listOne){
listOne.add(something);
}
以便多个线程一次访问一个listOne。
请参阅: http : //download.oracle.com/javase/tutorial/essential/concurrency/locksync.html
您需要了解锁定是建议性的,并且没有实际执行。 例如,如果您决定使用Object
来锁定对某些类字段的访问权限,则必须以这样的方式编写代码,以便在访问这些字段之前实际获取锁定。 如果不这样做,您仍然可以访问它们并可能导致死锁或其他线程问题。
例外情况是在方法上使用synchronized
关键字,运行时将自动为您获取锁定而无需执行任何特殊操作。
Java语言规范定义了synchronized
语句的含义,如下所示:
synchronized
语句代表执行线程获取互斥锁(第17.1节),执行块,然后释放锁。 当执行线程拥有锁时,没有其他线程可以获取锁。SynchronizedStatement:` synchronized ( Expression ) Block`
Expression的类型必须是引用类型,否则会发生编译时错误。
通过首先评估表达式来执行同步语句。
如果表达式的评估由于某种原因突然完成,则同步语句由于同样的原因而突然完成。
否则,如果Expression的值为null,则抛出NullPointerException。
否则,让表达式的非空值为V.执行线程锁定与V关联的锁。然后执行块。 如果块的执行正常完成,则解锁并且synchronized语句正常完成。 如果块的执行因任何原因突然完成,则锁定被解锁,然后同步语句突然完成,原因相同。
获取与对象关联的锁本身并不会阻止其他线程访问对象的字段或在对象上调用未同步的方法。 其他线程也可以以传统方式使用同步方法或同步语句来实现互斥。
也就是说,在你的例子中
synchronized(this) {
listOne.add(something);
}
synchronized块确实以任何特殊方式处理listOne
引用的对象,其他线程可以listOne
使用它。 然而,它确保没有其他线程可以进入同步块用于通过引用的对象this
同时。 因此,如果使用listOne
所有代码都在同一对象的同步块中,则在任何给定时间最多一个线程可以与listOne
一起使用。
另请注意,被锁定的对象没有特别保护其状态的并发访问,因此代码
void increment() {
synchronized (this) {
this.counter = this.counter + 1;
}
}
void reset() {
this.counter = 0;
}
错误地同步,因为第二个线程可能在第一个线程已读取但尚未写入counter
执行reset
,从而导致重置被覆盖。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.