【问题标题】:multithread state-dependent issue多线程状态相关问题
【发布时间】:2011-09-23 20:20:16
【问题描述】:

我得到了以下代码 sn-p:

public class ThreadTest{

  private static class Thread01 extends Thread{
    private Thread02 _th2; 
    public int foo = 0;

    public void setThrd02(Thread02 thrd2){
       _th2 = thrd2;
    }

    public void run(){
      try{
        for(int i=0;i<10;i++) foo += i;

        synchronized(this){this.notify();};

        synchronized(_th2){_th2.wait();};

        System.out.print(" Foo: " + _th2.foo);
      }catch(InterruptedException ie){ ie.printStackTrace();}    
    }
  }

  private static class Thread02 extends Thread{

    private final Thread01 _th1;
    public int foo = 0;

    public Thread02(Thread01 th1){
      _th1 = th1;
    }

    public void Run(){
       try{
         synchronized(_th1){_th1.wait();}
         foo = _th1.foo;

         for(int i=0;i<10;i++) foo += i;
         synchronized(this){this.notify();};
           }
       catch(InterruptedException ie){ie.printStackTrace();}

    }

  }

  public static void main(){
    Thread01 th1 = new Thread01();
    Thread02 th2 = new Thread02(th1);

    th1.setThrd02(th2);

    th1.start(); th2.start();
    th1.join(); th2.join();
  } 
}

我认为代码的假设和相应的目的就像 th2先运行,调用_th1.wait()变为等待状态; 然后th1计算foo并唤醒th2,th1进入等待状态; Th2 从 thread1 读取 foo 并更新为 110,然后唤醒 th1 和 th2 退出。 然后th1退出。

线程可能非常危险,因为线程 1 很可能先运行,而线程 2 将永远等待。

我不确定代码是否还有其他潜在问题。

可以解决问题的一种可能方法是,例如在线程1中

公共类 ThreadTest{

私有静态布尔更新=假; 私有静态布尔完成 = false;

私有静态 Thread01 扩展 Thread{

公共无效运行(){ // 进行计算 而(完成){ 等待(); } // 输出结果 } }

私有静态 Thread02 扩展 Thread{ 公共无效运行(){

而(假){ 等待(); }

foo = th1.foo; // 进行计算 // 类似于通知线程1的机制 } }

【问题讨论】:

  • 这里还有其他严重的问题吗?纠正它的最佳方法可能是什么?
  • 也许你想要一个CyclicBarrier,让线程在一个集合点相遇?
  • 还要注意,即使线程 2 先成功等待线程 1,然后线程 1 循环,通知线程 2,然后线程 2 将其循环添加到 sum,它仍然不会是 110 . 这是 0 到 9 的总和,而不是 0 到 10 的总和,所以总和将是 45,而不是 55,最后的总和是 90,而不是 110。
  • CyclicBarrier 在这种情况下可能有点复杂,以下是很好的解决方法:

标签: java multithreading concurrency


【解决方案1】:

无法保证您的线程中的顺序。 Thread01 过去就足够了 synchronized(this){this.notify();}; 在 Thread02 之前 synchronized(_th1){_th1.wait();} 让两个线程无限期地等待。

注意:您在 _th1 和 _th2 上调用 waitnotify 的事实无关紧要。这里的线程将被视为任何其他对象。

【讨论】:

    【解决方案2】:

    @Alex 已经指出了 wait 和 notify 没有按照代码期望的顺序调用 (+1) 的问题。然而,由于这是一个面试问题,这段代码还有其他一些问题:

    1. 糟糕的命名约定和代码格式,
    2. 公共字段访问器,
    3. 在 Thread 对象上同步(奇怪),
    4. 捕获 InterruptedException 然后退出线程,
    5. 无异常处理,
    6. (个人偏好)不使用 Java 并发库。

    我确定提出这个问题是为了让您陷入困境并找出并发性被破坏的原因,但是,恕我直言,该代码太可怕了,我什至都不会开始调试它 - 我只会把它扔掉.

    【讨论】:

      【解决方案3】:

      以下可能是更好的解决方法

      public class ThreadTest{
        private static volatile boolean updated = false; 
        private static volatile boolean finished = false;
      
      
         private static class Thread01 extends Thread{
          private Thread02 _th2; 
          public int foo = 0;
      
          public void setThread2(Thread02 th2){
              _th2 = th2;
          }
      
          public void Run(){          
      
              for(int i=0;i<10;i++) foo += i;
              System.out.print(" thread1 calcualtion " + foo + "\n");
      
              try{
              updated = true; 
              synchronized(this) {this.notify();};
      
              synchronized(_th2){
                   while(!finished) 
                       _th2.wait();
                   System.out.print("Foo: " + _th2.foo );    
                } 
          } 
          catch(InterruptedException ie){
              ie.printStackTrace();
          }
      }
       }
      
        private static class Thread02 extends Thread{ 
          private final Thread01 _th1;
          public int foo = 0;
      
          public Thread02(Thread01 th1){
              _th1 = th1;
          }
          public void run(){
          try{
              synchronized(_th1){
                  while(!updated) 
                      _th1.wait();
              foo = _th1.foo; 
              } 
          for(int i=0;i<10;i++) foo +=i; 
          finished = true; 
          synchronized(this){ this.notify();}
          }catch(InterruptedException ie){
              ie.printStackTrace();
          }
      }
       }
      
      public static void main(String[] args) {
          // TODO Auto-generated method stub
          Thread01 th1 = new Thread01();
          Thread02 th2 = new Thread02(th1);
      
          th1.setThread2(th2);
      
          try{
          th1.start();
          th2.start();
          th1.join();
          th2.join();
          }catch(InterruptedException ie){
              ie.printStackTrace();
          }
      }
      

      【讨论】:

      • 如果您不创建 updatedfinished volatile,每个级别(编译器、JIT、CPU、内存层次结构)的优化都会破坏您的代码。
      • 你能解释一下关于 volatile 的更多信息吗?看过java并发实践书,但是理解还不是很清楚。
      • 具有两个条件变量和 while 语句检查条件的解决方案有效,但看起来并不整洁。我在考虑 semapha 是否可以解决问题
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-19
      • 1970-01-01
      • 2011-03-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多