我正在查看包含同步方法的第三方库中的一些代码,并且在该方法中有一个同步块(synchronized block)锁定了一个实例变量。它与此类似:
public class Foo {
final Bar bar = new Bar();
public synchronized void doSomething() {
// do something
synchronized(bar) {
// update bar
}
}
...
}
这有意义吗?如果是这样,在同步方法中使用同步语句有什么好处?
鉴于同步方法锁定整个对象,这对我来说似乎是多余的。在处理非私有(private)实例变量时,这种方法是否有意义?
最佳答案
在您的示例中,该方法都锁定在Foo
的实例上在对象上 bar
.其他方法可能只锁定在 Foo
的实例上或对象 bar
.
所以,是的,这是否有意义取决于他们在做什么。大概是bar
保护一些较小的数据子集,一些方法只需要锁定 bar
以线程安全的方式执行它们的操作。
什么synchronized
做
synchronized
(在方法上或语句中)创建互斥区( critical section 或者,特别是对于 Java,reentrant mutex )。线程进入临界区的“关键”是synchronized
中使用的对象引用。声明‡。在所有使用相同 key 的 block 中,一次只能有一个线程(递归地)“拥有”这个“ key ”;也就是说,只有一个线程可以进入任何 block synchronized
一次在给定的对象引用上。
这样的关键部分只是防止您在 block 内执行的操作(变量读/写)与锁定同一对象引用的所有其他关键部分中的任何其他操作同时发生。 (它不会自动保护对象内的所有变量)。
在 Java 中,这样的临界区还会创建一个 happens-before契约(Contract)。
举例
作为一个有点做作的例子†:
public class Foo {
final Bar bar = new Bar();
private int instanceCounter = 0;
private int barCounter = 0;
public synchronized void incrementBarCounterIfAllowed() {
synchronized (bar) {
if (instanceCounter < 10) barCounter++;
}
}
public synchronized void incrementClassCounter() {
instanceCounter++;
}
public void incrementBarCounter() {
synchronized (bar) {
barCounter++;
}
}
}
实例变量是否私有(private)对于这种方法是否适用并不重要。在一个类中,您可以有多个锁对象,每个对象都保护自己的数据集。
但是这样做的风险是您必须非常严格遵守编码约定,以通过在不同位置以不同顺序锁定两个锁来防止死锁。例如,对于上面的代码,如果您随后从代码中的其他地方执行此操作:
synchronized(myFoo.bar) {
myFoo.incrementClassCounter();
}
你冒着与 incrementBarCounterIfAllowed()
陷入僵局的风险方法
† 请注意 barCounter
可以是 Bar
的实例变量等等等等 - 为了简洁起见,我在代码示例中避免了这种情况。
‡ 如果是synchronized
方法,该引用是对类实例的引用(或对 Class<?>
方法类的 static
的引用)。
关于java - 同步方法中的同步块(synchronized block),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32339001/