Java 两个线程,相同的静态变量,相同的值,并发访问

Java 两个线程,相同的静态变量,相同的值,并发访问,java,multithreading,concurrency,static,Java,Multithreading,Concurrency,Static,我一直在为下周的SCJP考试做准备,我遇到了一个关于Java线程的问题 1-public class Stone implements Runnable { 2- static int id = 1; 3- 4- public void run() { 5- try { 6- id = 1 - id; 7- if (id == 0) { 8- pick(); 9- } else { 10


我一直在为下周的SCJP考试做准备,我遇到了一个关于Java线程的问题

1-public class Stone implements Runnable {
2-  static int id = 1;
3-
4-  public void run() {
5-      try {
6-          id = 1 - id;
7-          if (id == 0) {
8-                      pick();
9-          } else {
10-             release();
11-         }
12-
13-     } catch (Exception e) {
14-     }
15- }
16-
17- private static synchronized void pick() throws Exception {
18-     System.out.print("P ");
19-     System.out.print("Q ");
20- }
21-
22- private synchronized void release() throws Exception {
23-     System.out.print("R ");
24-     System.out.print("S ");
25- }
26-
27- public static void main(String[] args) {
28-     Stone st = new Stone();
29-     new Thread(st).start();
30-     new Thread(st).start();
31- }
32-}
  • 哪个是真的?(选择所有适用的选项。)
  • 输出可以是pqrs
  • 输出可以是P-R-S-Q
  • 输出可以是prqs
  • 输出可以是pq
  • 该程序可能导致死锁
  • 编译失败
答案键上写着:
A、 B和C是正确的。由于pick()是静态的,release()是非静态的,因此 有两把锁。如果pick()是非静态的,则只有A是正确的。

它还说输出pq实际上不是一个选项,不可能得到这样的结果。

一开始,我并不相信答案键,但后来我发现,由于这个应用程序,真的不可能看到这个输出。(下课后)

现在,这是让我有点困惑的部分,这就是为什么

我认为一个pqpq或rsrs结果一定是可能的。因为总是有可能出现使两个线程的变量id完全相同的情况。换句话说,例如,当第一个线程刚刚执行完第6行时,它可以放弃轮到另一个线程,然后,另一个线程可以更改变量id的值,然后瞧!如果block愉快地,他们可以进入相同的位置

我一次又一次地试图看到这种情况(使用EclipseJuno和Java7)。这根本不会发生。我确信我的思维方式有问题,我想知道是什么问题我需要知道是什么规则阻碍这两个线程在相同状态下访问变量id。

当第一个线程刚刚完成执行第6行时,它可以放弃轮到另一个线程,然后,另一个线程可以更改变量id的值,然后瞧!如果布洛克高兴的话,他们也可以进去

假设线程1首先开始。它将id的值翻转为0。 线程1现在挂起在第8行

线程2启动。 它可以看到id的值

  • 1

    允许所有线程在本地缓存字段,除非它们标记为volatile。线程2缓存id的值

    线程2启动。它将值翻转为0。它们都进入第一个if块。 线程1在第7行被挂起。结果可能会有所不同

    输出可能是pq

  • 0

    它可以看到线程1中翻转的值

    再次将该值更改为1。进入else块

    备选方案A、B、C的情况


甚至不能保证线程1在线程2之前启动。

实际上,有很多可能性,有些可能性非常小,但它们仍然是可能的,在执行了100万次之后,这就是我发现的

代码:

我想我们已经证明了
pqpq
确实是可能的,尽管概率非常低,大约是百万分之一

[编辑:另一次运行,也可能出现显示
rs
的不同结果:]

done, results were:
 R S R S : 1
 R P S Q : 2
 P Q P Q : 1
 R S P Q : 445102
 P Q R S : 554877
 P R S Q : 5
 P R Q S : 2
 R P Q S : 10

是的,你是对的,
pq
是可能的

您可以通过以下修改来增加此事件的概率(它不会影响程序的语义):


在应用这些修改后,我在运行了几十次之后得到了
pq

是的,你的怀疑是对的。但是,run()方法中的代码非常简单,可以在一个CPU突发中执行,除非通过其他方式等待

你的假设是正确的。P Q P Q确实是可能的,因为声明如下:

在每个线程t执行的所有线程间操作中,t的程序顺序是一个总顺序,它反映了根据t的线程内语义执行这些操作的顺序

如果所有动作都以与程序顺序一致的总顺序(执行顺序)发生,则一组动作顺序一致,而且变量v的每个读取r都看到由写入w写入v的值,从而:

  • 在执行顺序中,w在r之前,并且
  • 在执行顺序中,没有其他写入w'使得w在w'之前,w'在r之前
顺序一致性是程序执行过程中可见性和顺序的有力保证。在顺序一致的执行中,所有单个操作(如读写)的总顺序与程序的顺序一致,每个单独的操作都是原子的,每个线程都可以立即看到


避免这种情况的最佳选择是。

RS绝对不可能,因为如果id==0,“RS”会被触发,而且它肯定不会自己到达那里。“pq pq”对我来说似乎是可能的-两个线程都看到id==1(run()不同步),两个调用pick()…当它们都运行时,rss是可能的,减小id,使其为-1,然后输入条件。但可能性很小,因为第一个线程必须在减量后中断。PQ不可能,因为只有一个线程可以在id值为0的第7行到达if语句。这是因为两个线程必须通过第6行,因此,如果有一个线程落后,那么当到达第7行时,它的值为-1。如果两个线程同时运行,那么当到达第7行时,它们的id值都将为-1。@Tsikon-nope。不要仅仅因为你把它看作是java的一行,就认为它是原子的。你可以把“id=1-id”想象成“int-temp=id-1;id=temp;”@Tsikon你到底是怎么找到-1值b的
Running 1000000 times...
50000... [R S P Q , P Q R S , P R S Q , R P Q S ]
100000... [R S P Q , P Q R S , P R S Q , R P Q S ]
150000... [R S P Q , P Q R S , P R S Q , R P Q S ]
200000... [R S P Q , P Q R S , P R S Q , R P Q S ]
250000... [R S P Q , P Q R S , P R S Q , R P Q S ]
300000... [R S P Q , P Q R S , P R S Q , R P Q S ]
350000... [R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
400000... [R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
450000... [R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
500000... [R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
550000... [R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
600000... [R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
650000... [R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
700000... [R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
750000... [R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
800000... [R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
850000... [R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
900000... [R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
950000... [P Q P Q , R S P Q , P Q R S , P R S Q , P R Q S , R P Q S ]
done, results were:
P Q P Q : 1
R S P Q : 60499
P Q R S : 939460
P R S Q : 23
P R Q S : 2
R P Q S : 15
done, results were:
 R S R S : 1
 R P S Q : 2
 P Q P Q : 1
 R S P Q : 445102
 P Q R S : 554877
 P R S Q : 5
 P R Q S : 2
 R P Q S : 10
public class Stone implements Runnable {
    static int id = 1;
    static CyclicBarrier b = new CyclicBarrier(2);

    public void run() {
        try {
            b.await(); // Increase probability of concurrent execution of subsequent actions

            int t = id;

            Thread.yield(); // Increase probability of thread switch at this point

            id = 1 - t;

            Thread.yield(); // Increase probability of thread switch at this point

            if (id == 0) {
                pick();
            } else {
                release();
            }
        } catch (Exception e) {}
    }
    ...
}