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是否能够解决这个问题