日期:2014-05-20  浏览次数:20704 次

java线程的notify方法,wait方法
以下代码本以为会重复输出 01直到结束,但是实际结果是:有时候能顺利正确的执行完,也有时候会执行到一点就卡住
不再继续往下执行,只能强制关闭程序。
public class B02Notify
{
public static void main(String[] args)
{
Sample sample = new Sample();

Increase in1 = new Increase(sample);
Increase in2 = new Increase(sample);
Decrease de1 = new Decrease(sample);
Decrease de2 = new Decrease(sample);

in1.start();
de1.start();
in2.start();
de2.start();
}
}
class Sample
{
private int a;

public synchronized void increase() 
{
while(a != 0)
{
try
{
wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}

a++;
System.out.println(a);

notify();

}

public synchronized void decrease()
{
while(a == 0)
{
try
{
wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}

a --;
System.out.println(a);

notify();
}
}

class Increase extends Thread
{
private Sample sample;

public Increase(Sample sample)
{
this.sample = sample;
}

@Override
public void run()
{
for(int i = 0; i < 20; i++)
{
sample2.increase2();
}
}
}

class Decrease extends Thread
{
private Sample sample;

public Decrease(Sample sample)
{
this.sample = sample;
}

@Override
public void run()
{
for(int i = 0; i < 20; i++)
{
sample.decrease();
}
}
}
然后,我将Decrease和Increase类中的run()都修改成:
         public void run()
{
for(int i = 0; i < 20; i++)
{
                          try
{
Thread.sleep((long)(Math.random() * 1000));
}
catch (InterruptedException e)
{
e.printStackTrace();
}
sample2.increase2();
}
}
     最终结果就是正确的循环输出1010101010........。  本人初涉java,望大鸟门指点迷境。
             
------解决方案--------------------
把notify改为notifyAll就行了
原因很简单:假如in1、in2、de1处于wait状态,del2执行完后如果唤醒的是del1的话,就in1、in2、de1、de2都wait了,死锁。所以只能换成notifyAll才能确保有in1或in2唤醒。
或者你可以用Lock来做,这个方便点。
------解决方案--------------------
引用:
sample2 increase2 decrease2  是我复制的时候弄错的。它们就是sample increase decrease,并没有改动。
改动的就只是在run方法中添加的随机睡眠, 但是代码可以顺利的跑,跑了十几次了,全部跑完。  
最后知道为什么会死锁之后,我更想明白为什么多了随机睡眠,就能顺利的跑完,不也应该会造成死锁的吗?


其实那只是恰好按照你期望的结果执行了,这种期望的结果只是在你当前机器的负荷下出现的概率比较大;
完全有可能发生你前面遇到的情况;
如:  
若de1  de2先运行,并进入sleep;
接着 in1  in2运行进入sleep;
接着操作系统繁忙,在de1  de2进入可运行状态时,没有运行;
接着 in1  in2进入可运行状态;
这样in1, in2,de1 , de2都是可运行状态;
这时若操作系统运行de1进入等待,运行de2进入等待,接着运行 in1 加1后唤醒de1,再次进入sleep(可以看成一个时间片断内),接着运行in2 进入等待,这时 in1 进入可运行状态并运行 in1 进入等待,唤醒的 de1 运行减一并唤醒de2,再次进入sleep,这时运行de2进入等待,de1进入可运行状态并运行de1进入等待

这其实与线程的调度有关:线程的调度是由操作系统控制的,而且是抢占式调度;

你要想使发生的几率变大,只需把睡眠时间改小就可以了:
如Increase改为:
Thread.sleep((long) (Math.random() * 10));

如Decrease改为:
Thread.sleep((long) (Math.random() * 15));

测试运行就会知道了;