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