Java 多线程状态相关问题

Java 多线程状态相关问题,java,multithreading,concurrency,Java,Multithreading,Concurrency,我收到了以下代码片段: 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{

我收到了以下代码片段:

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退出。 然后退出

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

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

解决此问题的一种可能方法是,例如在thread1中

公共类线程测试{

私有静态布尔更新=false; 私有静态布尔值finished=false

私有静态Thread01扩展线程{

公开募捐{ //计算 当完成{ 等待 } //输出结果 } }

私有静态Thread02扩展线程{ 公开募捐{

whilefalse{ 等待 }

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

您的线程中没有订购的保证。Thread01通过就足够了 synchronizedthis{this.notify;};在Thread02之前,synchronized_th1{u th1.wait;}使两个线程无限期地等待


注意:您在_-th1和_-th2上调用wait和notify这一事实与此无关。此处的线程将被视为任何其他对象。

无法保证在您的线程中进行排序。Thread01通过就足够了 synchronizedthis{this.notify;};在Thread02之前,synchronized_th1{u th1.wait;}使两个线程无限期地等待


注意:您在_-th1和_-th2上调用wait和notify这一事实是不相关的。这里的线程将被视为任何其他对象。

@Alex已经指出了wait和notify不按代码预期的+1顺序调用的问题。但是,由于这是一个访谈问题,因此还有一些其他问题需要解决使用此代码:

可怕的命名约定和代码格式, 公共字段访问器, 在线程对象上同步很奇怪, 捕获InterruptedException,然后退出线程, 没有异常处理, 个人偏好不使用Java并发库。
我敢肯定,这个问题的提出是为了让你纠结,弄清楚为什么并发性会被破坏,但是,恕我直言,这段代码太可怕了,我甚至都不会开始调试它——我就把它扔掉了。

@Alex已经指出了等待和通知的问题,没有按照代码预期的+1顺序调用。然而,由于这一点,我这是一个面试问题。这个代码还有其他一些问题:

可怕的命名约定和代码格式, 公共字段访问器, 在线程对象上同步很奇怪, 捕获InterruptedException,然后退出线程, 没有异常处理, 个人偏好不使用Java并发库。
我敢肯定,这个问题的提出是为了让你陷入困境,弄清楚为什么并发性会被破坏,但是,恕我直言,这段代码太可怕了,我甚至都不会开始调试它——我会把它扔掉。

下面是一个更好的修复方法

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();
    }
}

下面是一个更好的解决方案

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();
    }
}

这里还有其他严重的问题吗?什么可能是纠正它的最佳方法?也许您需要一个CyclicBarrier,使线程在集合点相遇?还要注意,即使线程2成功地先等待线程1,然后线程1执行其循环,通知线程2,然后线程2将其循环添加到总和,它仍然不会是110。这是t加起来是0到9,而不是0到10,所以加起来是45,而不是55,总的来说是90,而不是110。CyclicBarrier在这种情况下可能有点复杂,下面是一个很好的解决方法:这里还有其他严重的问题吗?什么可能是最好的纠正方法?也许你想要一个CyclicBarrier,让线程在集合点相遇int?还要注意,即使线程2首先成功地等待线程1,然后线程1执行其循环,通知线程2,然后线程2将其循环添加到总和中,它仍然不会是110。这是0到9的总和,而不是0到10的总和,因此总和将是45,而不是55,使得总数在90结尾,而不是110。CyclicBarrier可能有点复杂在这种情况下,以下可能是一个很好的解决方案:如果你不使更新和完成的易失性,每一级编译器、JIT、CPU、内存层次结构的优化都会破坏你的代码。你能解释一下易失性吗?我读了《java并发性练习》一书,但理解仍然不是很清楚。解决方案有两个方面带while语句检查条件的条件变量可以工作,但它看起来并不整洁。我在想,如果不使更新和完成的条件变为易失性,semapha是否能够解决这个问题,编译器、JIT、CPU和内存层次结构的每个级别的优化都可以
破译你的密码。你能再解释一下易变性吗?我读过《java并发性练习》一书,但对它的理解还不是很清楚。使用while语句检查条件的两个条件变量的解决方案是可行的,但看起来并不整洁。我在想semapha是否能够解决这个问题