为什么必须指定哪个对象锁定了同步代码块?
您不必指定哪个对象锁定了同步方法,因为它总是被“this”锁定(我相信)。
我有两个问题:
- 为什么不能用 'this' 以外的对象阻止非静态方法?
- 为什么必须指定阻塞同步代码的对象?
我已经阅读了 SCJP for Java 6 的第九章,但我仍然不清楚这一点。
我意识到这可能是一个基本问题,但我是线程新手。
为什么必须指定哪个对象锁定了同步代码块?
您不必指定哪个对象锁定了同步方法,因为它总是被“this”锁定(我相信)。
我有两个问题:
我已经阅读了 SCJP for Java 6 的第九章,但我仍然不清楚这一点。
我意识到这可能是一个基本问题,但我是线程新手。
为什么不能用 'this' 以外的对象阻止非静态方法?
你可以:
public void foo() {
synchronized (lock) {
...
}
}
为什么必须指定阻塞同步代码的对象?
因为这就是语言设计者选择设计语言的方式。synchronized
,在实例方法上使用时,隐式this
用作锁。synchronized
在块上使用时必须明确指定锁。
你可以。编码
synchronized foo() {
// some stuff
}
逻辑上等于代码
foo() {
synchronized(this) {
// some stuff
}
}
我说“逻辑上”是因为这两个示例生成不同的字节码。
如果方法foo()
是静态同步,则对类对象进行。
但是,您可能希望创建多个同步块,这些块在不同对象上同步到一个类甚至一个方法中。在这种情况下,您可以使用synchronized (lock)
where lock
is not this
:
foo() {
synchronized(one) {}
///..........
synchronized(two) {}
}
我认为你的两个问题实际上是相同的。假设你想在多个线程之间同步对象 a 的方法 m,你需要一个线程可以与之通信的公共基础系统或通道,这实际上是对象锁提供的,所以当你想在那里同步对象的方法时不需要另一个对象锁来执行此操作,因为您访问的对象本身就有这个锁,这就是语言设计的方式和原因。
虽然同步一个块不是一回事,线程可以有不同的谈话基础,而不是这个对象本身,例如,你可以为同步块设置一个相同的对象作为同步对象锁,这样这个类的所有对象都可以同步堵塞!
this
或locker
...考虑:
a = 1;
a++;
线程一个到达第二行,您期望a = 2
但另一个线程执行第一行,而不是a = 1
第一个线程和a = 2
第二个线程的 2。现在:
synchronized (whatever)
{
a = 1;
a++;
}
现在第二个线程将被阻止进入code block
(synchronized
主体),直到第一个离开它(释放锁)。
您可以指定任何需要锁定同步代码块的对象。实际上,您根本不应该使用synchronize(this)
(或者可能要小心,请参阅Java 中的避免同步(this)?)。
每个对象都有一个可以同步的锁:
final Object lock = new Object() synchronized ( lock ) { ... }
如果你想同步整个方法,你不能说在哪个对象上,所以它总是“this”对象。
同步 foo() {....}
顺便说一句,第一种锁定方式更好。
不建议锁定每个方法,this
因为它在大多数情况下会降低并发性。因此建议使用Lock Stripping,其中仅将需要保护的代码的特定部分保存在同步块中。
这是一种实践,在Java Concurrency in Practice中有很好的解释。但请注意,这本书只有在您对线程有一些基本经验时才有用。
要记住的一些掘金:
使用不同的锁来保护两个不相关的实体,这会增加并发的机会。否则读取或写入两个不相关的实体线程将阻塞在同一个锁上。
public void incrementCounter1(){
synchronized(lockForCounter1){
counter1++;
}
}
public void incrementCounter2(){
synchronized(lockForCounter2){
counter2++;
}
}
要使方法同步,只需将 synchronized 关键字添加到其声明中:
public class SynchronizedCounter {
private int c = 0;
public synchronized void increment() {
c++;
}
public synchronized void decrement() {
c--;
}
public synchronized int value() {
return c;
}
}
如果 count 是 SynchronizedCounter 的一个实例,那么使这些方法同步有两个效果:
首先,同一对象上的同步方法的两次调用不可能交错。当一个线程正在为一个对象执行同步方法时,所有其他为同一对象调用同步方法的线程都会阻塞(暂停执行),直到第一个线程处理完该对象。
其次,当同步方法退出时,它会自动与任何后续对同一对象的同步方法调用建立起之前的关系。这保证了对象状态的更改对所有线程都是可见的。
简而言之,这就是 Java 中同步的工作原理。
您不必指定哪个对象锁定了同步方法,因为它总是被“this”锁定(我相信)。
对于实例方法是正确的,对于静态方法,它锁定在类对象上。
为什么不能用 'this' 以外的对象阻止非静态方法?
注意:
public synchronized void increment() {
c++;
}
在行为上等同于:
public void increment() {
synchronized(this) {
c++;
}
}
在此代码段中,您可以替换this
为您想要的任何对象。
为什么必须指定阻塞同步代码的对象?
有些,我们称之为关键,代码块只能按顺序运行,但在不受控制的并发环境中,它可能会并行运行。这就是存在同步机制的原因。
所以我们可以这样划分代码:
该标记是通过synchronized
关键字和相应的锁定对象进行的。
如果您有两个不能一起运行的不同关键代码块,它们都将具有 synchronized 关键字,假设它们具有相同的锁定对象。
在执行第一个块时,锁定对象变为“锁定”。如果在此期间需要执行第二个块,则该代码块的第一个命令是:
synchronized(lock) {
但是该锁定对象处于锁定状态,因为第一个块正在执行。第二个块的执行在该语句上停止,直到第一个块完成执行,并解锁锁定对象的状态。然后第二个块可以继续执行(并再次锁定锁定对象)。
这种机制称为互斥,锁是一个通用概念,与 Java 编程语言无关。
“锁定对象锁定”过程的详细信息可以在这里找到。