class Notification(val context: Context, title: String, message: String) {
private val channelID = "TestMessages"
companion object ID {
var s_notificationID = -1
}
init {
var notificationID = -1
synchronized(s_notificationID) {
if (++s_notificationID == 0)
createNotificationChannel()
notificationID = s_notificationID
}
Run Code Online (Sandbox Code Playgroud)
以上是从两个线程同时调用的。中的断点createNotificationChannel()清楚地表明有时s_notificationID等于 1。
但是,如果我更改
synchronized(s_notificationID)
为synchronized(ID)
那么它似乎锁定得很好。
synchronized() 是否不锁定基本类型?如果是这样,为什么要编译?
查看生成的 JVM 字节码表明该ID示例看起来像
synchronized(ID) { ... }
Run Code Online (Sandbox Code Playgroud)
这是你所期望的。然而,这个s_notificationID例子看起来更像
synchronized(Integer.valueOf(s_notificationID)) { ... }
Run Code Online (Sandbox Code Playgroud)
在 Java 中,我们只能同步对象,而不能同步原语。Kotlin 基本上消除了这种区别,但看起来您已经找到了一个实现仍然渗透的地方。由于s_notificationID就intJVM 而言是一个(因此,不是对象)但synchronized需要一个对象,因此 Kotlin 足够“聪明”,可以Integer.valueOf按需包装值。对您来说不幸的是,这会产生非常不一致的结果,因为
此方法将始终缓存 -128 到 127(含)范围内的值,并且可能缓存此范围之外的其他值。
因此,对于较小的数字,这可以保证锁定内存中您无法控制的某些缓存对象。对于大型对象,它可能是一个新对象(因此总是未锁定),或者它可能会再次出现在您手中的缓存对象上。
这里的教训似乎是:不要同步原始类型。
Silvio Mayolo 解释了为什么在基元上同步不是一个好主意(实际上,我认为编译器应该对此发出警告)。但我相信这段代码还有另一个问题,可能是使您的synchronized块并行工作的主要问题。
问题是您替换了 的值s_notificationID。即使它是一个对象,而不是基元,您的同步块仍然会并行运行,因为每次调用都synchronized使用不同的对象。this这就是为什么在 Java 中我们通常在需要修改的字段上进行同步而不是同步。
| 归档时间: |
|
| 查看次数: |
116 次 |
| 最近记录: |