我想通过多个 API 请求锁定一个对象,这样每个用户只有一个请求可以输入一段代码。
是否synchronized(obj)
基于对象的引用或其hashCode()
功能锁定?
即我可以这样做:
synchronized("asdf") {
doSomethingNifty();
}
这里“asdf”有一个唯一的散列但没有唯一的引用。
我想通过多个 API 请求锁定一个对象,这样每个用户只有一个请求可以输入一段代码。
是否synchronized(obj)
基于对象的引用或其hashCode()
功能锁定?
即我可以这样做:
synchronized("asdf") {
doSomethingNifty();
}
这里“asdf”有一个唯一的散列但没有唯一的引用。
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”的使用中具有唯一的“内存地址” 。这意味着一些完全不同的类也可能具有相同的错误模式代码块,并且会影响类的同步,因为它会锁定在同一个对象实例上。这就是锁对象如此重要的原因。String
private
当我们讨论这个主题时,你也绝不能同步一个可变值,比如像Boolean
or的非最终对象Integer
。下面的模式经常被使用并且是非常错误的:
Boolean value = false;
...
// really bad idea
synchronized (value) {
if (value) {
value = false;
} else {
value = true;
}
}
这是非常错误的,因为value
参考正在改变。因此,一个线程可能会锁定它,然后更改它的参考值,以便另一个线程将锁定另一个对象,并且两个线程将同时在其中synchronized
。更糟糕的是,因为Boolean
只有 2 个值true
和false
是常量,所以多个类将锁定相同的引用。
您正在对一个对象进行同步,内存地址问题纯粹是一个实现问题,与您无关。只要它是同一个对象(意味着完全相同的实例),同步就完成了。
如果您使用不同的实例,同步将不起作用。您可以做的是定义一个公共静态常量作为锁:
public final static Object LOCK = new Object();
并使用它
synchronized(SomeClass.LOCK) {
锁位于对象本身的实例上,如果您想这样考虑,可以将其视为内存位置。所以你的建议是行不通的。
相反,听起来您只需要一个与您要保护的代码块相对应的对象。因此,在您的程序设置过程中的某个地方,您需要类似的东西
static Object lockObject = new Object();
然后你可以做
synchronized(lockObject) {
doSomethingNifty();
}
但是,如果 doSomethingNifty() 与特定对象相关,那么使用该对象而不是 lockObject 会很有意义。还要确保 doSomethingNifty() 快速执行,否则会有很多用户等待。