关于notify() 和notifyAll() 一个需要注意的地方

notify() 和 notifyAll()都是唤醒其他正在等待同一个对象锁的线程。

下面是我遇到的一个问题,记下来,免得忘了。

直接上代码,有错误的代码:

代码描述:有一个Caculate类,类中又一个成员变量 j,现在有多个线程对这个变量进行操作。一个增加操作、一个减少操作。增加操作:当 j = 0 时,j++ 。减少操作:当 j = 1 时,j-- 。这两个操作分别对应这 add()方法 和sub()方法,都使用synchronized关键字。

可以直接复制拿来运行一下

package com.zcd2;

public class ThreadTest1
{
    public static void main(String[] args)
    {
        Caculate caculate = new Caculate();
        
        //使用多个线程对实例caculate进行增加操作。
        for(int i = 0; i < 10; i++)
        {
            Thread1 t = new Thread1(caculate);
            t.start();
        }
        
        //使用多个线程对实例caculate进行减少操作。
        for(int i = 0; i < 2; i++)
        {
            Thread2 t = new Thread2(caculate);
            t.start();
        }
    }
}

//Thread1线程进行增加操作
class Thread1 extends Thread
{
    private Caculate caculate;
    
    public Thread1()
    {
        
    }    
    
    public Thread1(Caculate caculate)
    {
        this.caculate = caculate;
    }
    
    @Override
    public void run()
    {
        int i = 0;

        //死循环,手动停止
        while(true)
        {
            try
            {
                caculate.add();
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            i++;
            System.out.println("加线程执行第 " + i + " 次");
        }
    }    
}

//Thread2进行减少操作。
class Thread2 extends Thread
{    
    private Caculate caculate;
    
    public Thread2()
    {
    }

    public Thread2(Caculate caculate)
    {
        this.caculate = caculate;
    }

    @Override
    public void run()
    {    
        int i = 0;
        
        //死循环,手动停止
        while(true)
        {
            try
            {
                caculate.sub();
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            i++;
            System.out.println("减线程执行第 " + i + " 次");
        }
    }    
}

//
class Caculate
{
    private int j = 0;    

    //增加操作
    public synchronized void add() throws InterruptedException
    {
     //当 j = 1 的时候说明不符合操作条件,要放弃对象锁。
        while(j == 1)
        {
            wait();
            System.out.println();
        }    
        j++;
        System.out.println(j);
        notify();
    }

    //减少操作
    public synchronized void sub() throws InterruptedException
    {
     //当j = 0 的时候说明不符合操作条件,放弃对象锁
        while(j == 0)
        {
            wait();
            System.out.println();
        }
        j--;
        System.out.println(j);
        notify();
    }
}

以上代码并不能一直循环执行,按道理说应该是一直循环执行的。

为什么呢????????

这就涉及到了notify() 和 notifyAll()的其中一个区别了。

这个区别就是:调用 notify() 方法只能随机唤醒一个线程,调用notifyAll() 方法的唤醒所有的等待的线程。

比如这里,当一个线程在正常执行。。。假设这里正常执行完一个增加操作的线程,然后调用 notify() 方法 那么它会随机唤醒一个线程。

  ①、如果唤醒的是进行减少操作的线程,此时 j = 1,线程能够正常执行减少操作。

  ②、如果唤醒的是进行增加操作的线程,此时 j = 1,那么不符合增加操作的条件,他就会调用 wait() 方法。那么调用完wait()方法后程序就会发现已经没有被唤醒的线程了。唯一一个被唤醒的线程因不符合条件放弃了对象锁,其他线程又没有被唤醒。此时程序只能一直等到其他线程被唤醒,但是它等不到了。

解决:

把notify() 改成notifyAll() 这个问题就解决了。因为如果唤醒一个线程,但是这个线程因不符合执行条件而放弃对象,还有很多唤醒的线程。

所以,当多个(两个以上的)线程操作同一个对象的时候最好使用的notifyAll(),这样就不会出现上述的问题了。


发现一个问题,既然使用notify()会出问题那为什么不在每个地方的使用notifyAll()呢??这二者还有其他我没了解的区别吗???难道使用notifyAll() 会使性能大大下降???有待解决。

posted @ 2017-10-10 19:52  钓鱼翁  阅读(1331)  评论(0编辑  收藏  举报