当String是锁的对象时,String.intern()和Striped哪个更好?
When String is the object of the lock, which is better String.intern() or Striped?
想象一个场景,我们需要根据设备id来加锁,设备id是一个字符串。很多人推荐使用String.intern()
作为锁对象,但是也有人推荐使用Striped
进行并发控制,比如下面的代码:
import com.google.common.util.concurrent.Striped;
import java.util.concurrent.locks.Lock;
public class Test {
private Striped<Lock> striped = Striped.lock(8);
public void businessLogicByStringIntern(String deviceId) {
// use deviceId.intern() as lock
synchronized (deviceId.intern()) {
// execute code thread-safely
}
}
public void businessLogicByStriped(String deviceId) {
// use striped lock
synchronized (striped.get(deviceId)) {
// execute code thread-safely
}
}
}
更推荐哪个实现,businessLogicByStringIntern
或 businessLogicByStriped
?
参考:
Striped (Guava: Google Core Libraries for Java 19.0 API)
在第一个版本中,每个设备都有一个唯一的锁,由 deviceId
字符串区分。在这种情况下 intern
是必需的。
在第二个版本中,您只有(比方说)8 把锁,并且它们在所有设备上都是条带化的。
有什么区别?
第一个版本创建了更多的原始锁。但是原始锁很便宜,除非存在锁争用。使用此版本,如果两个或多个线程确实试图对同一设备执行某些操作,则只会发生锁争用。
第二个版本不会创建超过 8 个锁,它们是 Lock
个对象。 Lock
API 提供了比原始锁更多的功能......如果这对你有用的话。
但是对于这个版本,你会得到更多的争论。例如,如果两个不同的设备使用相同的条带锁,那么这两个设备就会相互排斥......这可能是您不想要的。
使用内部字符串作为锁标识符也存在理论上的问题。 interned 字符串的 space 是 JVM 范围的,因此如果您的应用程序的不同部分 独立地 以这种方式锁定事物(例如使用 interned 设备 id 字符串),它们可能干扰。应用程序的两个部分可能会意外地共享锁。 (这个问题在 中有更深入的讨论)
如果这是一个真正的问题,有一些方法可以避免这种情况。例如,应用程序的两个部分可以在实习之前向字符串添加(不同的)前缀等。
哪个更好?
好吧,这取决于 1) 您要优化的内容以及 2) 锁可能持有多长时间;即由条带化引起的不必要争用的成本。
在大多数情况下,第一个版本(每个设备一个不同的锁)会更好。但是,如果您有大量设备,那么大量驻留字符串的成本可能会很高。在那种情况下,如果锁持有时间很短,那么锁条带化可能会更好。
想象一个场景,我们需要根据设备id来加锁,设备id是一个字符串。很多人推荐使用String.intern()
作为锁对象,但是也有人推荐使用Striped
进行并发控制,比如下面的代码:
import com.google.common.util.concurrent.Striped;
import java.util.concurrent.locks.Lock;
public class Test {
private Striped<Lock> striped = Striped.lock(8);
public void businessLogicByStringIntern(String deviceId) {
// use deviceId.intern() as lock
synchronized (deviceId.intern()) {
// execute code thread-safely
}
}
public void businessLogicByStriped(String deviceId) {
// use striped lock
synchronized (striped.get(deviceId)) {
// execute code thread-safely
}
}
}
更推荐哪个实现,businessLogicByStringIntern
或 businessLogicByStriped
?
参考:
Striped (Guava: Google Core Libraries for Java 19.0 API)
在第一个版本中,每个设备都有一个唯一的锁,由 deviceId
字符串区分。在这种情况下 intern
是必需的。
在第二个版本中,您只有(比方说)8 把锁,并且它们在所有设备上都是条带化的。
有什么区别?
第一个版本创建了更多的原始锁。但是原始锁很便宜,除非存在锁争用。使用此版本,如果两个或多个线程确实试图对同一设备执行某些操作,则只会发生锁争用。
第二个版本不会创建超过 8 个锁,它们是
Lock
个对象。Lock
API 提供了比原始锁更多的功能......如果这对你有用的话。但是对于这个版本,你会得到更多的争论。例如,如果两个不同的设备使用相同的条带锁,那么这两个设备就会相互排斥......这可能是您不想要的。
使用内部字符串作为锁标识符也存在理论上的问题。 interned 字符串的 space 是 JVM 范围的,因此如果您的应用程序的不同部分 独立地 以这种方式锁定事物(例如使用 interned 设备 id 字符串),它们可能干扰。应用程序的两个部分可能会意外地共享锁。 (这个问题在 中有更深入的讨论)
如果这是一个真正的问题,有一些方法可以避免这种情况。例如,应用程序的两个部分可以在实习之前向字符串添加(不同的)前缀等。
哪个更好?
好吧,这取决于 1) 您要优化的内容以及 2) 锁可能持有多长时间;即由条带化引起的不必要争用的成本。
在大多数情况下,第一个版本(每个设备一个不同的锁)会更好。但是,如果您有大量设备,那么大量驻留字符串的成本可能会很高。在那种情况下,如果锁持有时间很短,那么锁条带化可能会更好。