是id = 1 – idprimefaces?
从OCP Java SE 6程序员实践考试第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(); } }
其中一个答案是:
输出可以是
PQPQ
我标记这个答案是正确的。 我的推理:
- 我们开始两个线程。
- 首先进入
run()
。 - 根据JLS 15.26.1 ,它首先评估
1 - id
。 结果是0
。 它存储在线程的堆栈中。 我们正要将0
保存为静态id
,但是… - 繁荣,调度select第二个线程运行。
- 所以,第二个线程进入
run()
。 静态id
仍然是1
,所以他执行pick()
方法。 打印PQ
。 - 调度程序select第一个线程运行。 它从堆栈中取
0
,并保存到静态id
。 所以,第一个线程也执行pick()
并打印PQ
。
然而,在书中写道,这个答案是不正确的:
这是不正确的,因为行
id = 1 - id
交换0
和1
之间的id
的值。 相同的方法没有机会被执行两次。
我不同意。 我想上面提到的场景有一些机会。 这种交换不是primefaces的。 我错了吗?
我错了吗?
不,你是绝对正确的 – 正如你的例子时间表。
除了不是primefaces的,不能保证write的id
会被其他线程拿走,因为没有同步,而且字段也不是不稳定的。
像这样的参考资料是不正确的有点令人不安
在我看来,实践考试的答案是正确的。 在这段代码中,你正在执行两个可以访问相同静态variablesid的线程。 静态variables存储在java中的堆中,而不是堆栈中。 runnables的执行顺序是不可预知的。
但是,为了改变每个线程的id值:
- 使存储在ID的存储器地址中的值的本地副本到CPUregistry;
- 执行操作
1 - id
。 严格来说,这里执行两个操作(-id and +1)
; - 将结果移回到堆的
id
内存空间。
这意味着虽然id值可以被两个线程中的任何一个同时改变,但是只有初始值和最终值是可变的。 中间值不会彼此修改。
此外,对代码的分析可以表明,在任何时候,id只能是0或1。
certificate:
-
起始值id = 1; 一个线程会将其更改为0(
id = 1 - id
)。 而另一个线程将把它带回到1。 -
起始值id = 0; 一个线程会将其更改为1(
id = 1 - id
)。 而另一个线程将把它带回到0。
因此,id的值状态是离散的0或1。
结束certificate。
这个代码有两种可能性:
-
可能性1.线程先访问variablesID。 然后,id(
id = 1 - id
值变为0.此后,只有方法pick ()
会被执行,打印PQ
。线程2,会在这个时候评估id = 0
;方法release()
执行打印R S。结果,PQRS
将被打印。 -
可能性2.首先访问两个variablesid。 然后,id(
id = 1 - id
值变为0.此后,只有方法pick ()
会被执行,打印PQ
。线程1,将在此时评估id = 0
;方法release()
执行打印R S。结果,PQRS
将被打印。
没有其他的可能性。 但是,应该注意的是,由于pick()
是一个静态方法,所以可以打印诸如PRQS
或RPQS
等的PQRS
变体,因此在两个线程之间共享。 这会导致同时执行此方法,这可能导致根据您的平台以不同的顺序打印字母。
然而在任何情况下, pick()
或release ()
不会执行两次,因为它们是互斥的 。 所以PQPQ
不会是输出。