【发布时间】:2026-02-19 12:25:03
【问题描述】:
代码 sn-p - 1
class RequestObject implements Runnable
{
private static Integer nRequests = 0;
@Override
public void run()
{
synchronized (nRequests)
{
nRequests++;
}
}
}
代码 sn-p - 2
public class Racer implements Runnable
{
public static Boolean won = false;
@Override
public void run()
{
synchronized (won)
{
if (!won)
won = true;
}
}
}
我遇到了第一个代码 sn-p 的竞争条件。我understood 这是因为我正在获得一个不可变对象(整数类型)的锁。
我已经编写了第二个代码 sn-p,它再次不受“布尔”不可变的影响。但这有效(输出运行中不显示竞争条件)。如果我已经正确理解了我的previous question 的解决方案,那么以下是可能出错的一种可能方式
- 线程 1 获得
won指向的对象(比如 A)上的锁 - 线程 2 现在尝试锁定
won指向的对象并进入 A 的等待队列 - 线程 1 进入同步块,验证 A 是否为假,并通过说
won = true(A 认为它赢得了比赛)创建了一个新对象(比如 B)。 - 'won' 现在指向 B。线程 1 释放对象 A 上的锁(
won不再指向) - 现在,对象 A 的等待队列中的线程 2 被唤醒并获得对象 A 的锁定,该对象 A 仍然是
false(不变)。它现在进入同步块并假设它也赢了,这是不正确的。
为什么第二个代码 sn-p 一直运行良好??
【问题讨论】:
-
这与您之前的问题有何不同?
-
此代码示例看起来相似,但(似乎)表现不同。我书中有效的后续问题。
-
我想你误解了不可变的含义。要使某些东西不可变,您应该将其标记为 final
-
@luketorjussen 这不是真的。不可变意味着一旦创建,您就无法更改对象的状态。例如,没有
set方法可以像 String、Integer、Boolean 那样更改对象... 不可变与final无关 -
一个令人困惑的地方是隐式装箱在两个示例中都在进行。这会导致变量的任何“修改”以用新对象替换对象。上面两个例子都有这个问题(用一个新的替换锁对象),只是第二个例子没有需要同步的东西(可以完全删除
synchronized语句而没有外部影响),所以这个问题永远不会产生“意外”的结果。
标签: java multithreading synchronization