【发布时间】:2015-01-21 05:48:27
【问题描述】:
来自 OCP Java SE 6 Programmer Practice Exams 的第 291 页,问题 25:
public class Stone implements Runnable {
static int id = 1;
public void run() {
id = 1 - id;
if (id == 0)
pick();
else
release();
}
private static synchronized void pick() {
System.out.print("P ");
System.out.print("Q ");
}
private synchronized void release() {
System.out.print("R ");
System.out.print("S ");
}
public static void main(String[] args) {
Stone st = new Stone();
new Thread(st).start();
new Thread(st).start();
}
}
答案之一是:
输出可能是
P Q P Q
我将此答案标记为正确。我的推理:
- 我们正在启动两个线程。
- 第一个输入
run()。 - 根据JLS 15.26.1,它首先评估
1 - id。结果是0。它存储在线程的堆栈中。我们正准备将0保存为静态id,但是... - Boom,调度程序选择了第二个线程来运行。
- 所以,第二个线程进入
run()。静态的id还是1,所以他执行方法pick()。P Q已打印。 - 调度程序选择第一个线程来运行。它从堆栈中取出
0并保存到静态id。因此,第一个线程也执行pick()并打印P Q。
但是,书中写到这个答案是不正确的:
这是不正确的,因为
id = 1 - id行在0和1之间交换了id的值。同一个方法不可能被执行两次。
我不同意。我认为我上面介绍的场景有一些机会。这种交换不是原子的。我错了吗?
【问题讨论】:
-
顺便说一句,他们允许 R S R S 吗?
-
@JonSkeet 没有这样的答案。他们允许
P Q R S、P R S Q和P R Q S,我同意。 -
我认为您将您所指的 JLS 部分断章取意。该部分介绍了简单的分配(如在单个线程中)。我认为您需要查看JLS 17.4. Memory Model。
-
当然
P R S Q和P R Q S也不可能,因为pick和release是同步的。我错过了什么吗(我的 Java 可能有点生疏了)? -
在原始代码示例(来自提到的书)中,
release方法不是静态的。所以P R S Q和P R Q S是 确实可能的解决方案。然而,这并不能解决run方法中的竞争条件,因此本书在这个问题上仍然是错误的。
标签: java multithreading atomic swap scjp