7

我想通过多个 API 请求锁定一个对象,这样每个用户只有一个请求可以输入一段代码。

是否synchronized(obj)基于对象的引用或其hashCode()功能锁定?

即我可以这样做:

synchronized("asdf") {
    doSomethingNifty();
}

这里“asdf”有一个唯一的散列但没有唯一的引用。

4

3 回答 3

12

synchronized(obj) 是否基于对象的内存位置或其 toHash() 函数锁定?

两者都不。它锁定在与对象关联的监视器上。就 JVM 而言,我们不谈论对象的内存地址,因为它是可重定位的,而且它不是哈希码(即使是Object.hashcode()),因为它不是唯一的。

就您应该锁定的内容而言,它应该是同一个final对象。就像是:

private final Object lockObject = new Object();
...
synchronized (lockObject) {
   // do stuff that needed to be protected
}

您希望这样final可以保证多个线程锁定在不变的同一个对象引用上。 private很好,所以外面的班级不能搞砸班级内部的锁定。

这里“asdf”有一个唯一的散列但没有唯一的内存地址。

"asdf"没有唯一的散列,因为其他字符串可能具有相同的散列,并且如果编译器将其存储在 Java 字符串池中,它实际上可能应用程序中所有“asdf”的使用中具有唯一的“内存地址” 。这意味着一些完全不同的类也可能具有相同的错误模式代码块,并且会影响类的同步,因为它会锁定在同一个对象实例上。这就是锁对象如此重要的原因。Stringprivate

当我们讨论这个主题时,你也绝不能同步一个可变值,比如像Booleanor的非最终对象Integer。下面的模式经常被使用并且是非常错误的:

Boolean value = false;
...
// really bad idea
synchronized (value) {
   if (value) {
      value = false;
   } else {
      value = true;
   }
}

这是非常错误的,因为value参考正在改变。因此,一个线程可能会锁定它,然后更改它的参考值,以便另一个线程将锁定另一个对象,并且两个线程将同时在其中synchronized。更糟糕的是,因为Boolean只有 2 个值truefalse是常量,所以多个类将锁定相同​​的引用。

于 2013-05-21T16:33:06.140 回答
2

您正在对一个对象进行同步,内存地址问题纯粹是一个实现问题,与您无关。只要它是同一个对象(意味着完全相同的实例),同步就完成了。

如果您使用不同的实例,同步将不起作用。您可以做的是定义一个公共静态常量作为锁:

public final static Object LOCK = new Object();

并使用它

synchronized(SomeClass.LOCK) {
于 2013-05-21T16:32:46.950 回答
0

锁位于对象本身的实例上,如果您想这样考虑,可以将其视为内存位置。所以你的建议是行不通的。

相反,听起来您只需要一个与您要保护的代码块相对应的对象。因此,在您的程序设置过程中的某个地方,您需要类似的东西

static Object lockObject = new Object();

然后你可以做

synchronized(lockObject) {
    doSomethingNifty();
}

但是,如果 doSomethingNifty() 与特定对象相关,那么使用该对象而不是 lockObject 会很有意义。还要确保 doSomethingNifty() 快速执行,否则会有很多用户等待。

于 2013-05-21T16:33:40.077 回答