java并发学习--第六章 线程之间的通信

一、等待通知机制wait()与notify()

  在线程中除了线程同步机制外,还有一个最重要的机制就是线程之间的协调任务。比如说最常见的生产者与消费者模式,很明显如果要实现这个模式,我们需要创建两个线程,一个生产者,一个消费者;有两个线程还不够,如果当生产者生产商品完成后,消费者如何知道要去消费生产的商品?为此JDK给我们可提供了wait()和notify()方法来进行线程之间的通信,从而解决了生产者完成后通知消费者进行消费的问题。

  wait()和notify()是等待/唤醒机制,调用wait()方法可以让当前线程阻塞,只有当使用notify()时,才能让阻塞的线程继续执行。这样的机制就能够达道两个线程之间通信的功能。

  我们来看看这两个方法:

  1.wait()、notify()和notifyAll()方法是都是Objec类中自带的方法,因此所有的类都能够使用;

 

  2.wait()、notify()和notifyAll()是native方法,无法被重写;

 

  3.调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的锁;

 

  4.notify()方法能够唤醒一个正在等待的线程,如果有多个线程都在等待被唤醒,调用notify()方法只能随机唤醒一个方法;

 

  5.调用notifyAll()方法能够唤醒所有正在等待的线程;

 

  6.使用wait()、notify()和notifyAll()方法必须给线程加锁。

   

  我们用一个例子来介绍生成者与消费者模式:

  生产者代码:

 

/**
 * 生产者,生成了10个商品后通知消费者消费
 */
public class Productor implements Runnable {
    
    @Override
    public void run() {
        //注意,这里生成者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
        synchronized (Job.value) {
            int i = 0;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            while (i < 10) {
                //生产任务
                Job.value[i] = "商品:" + i;
                System.out.println("开始生产" + Job.value[i]);
                i++;
            }
            //生产完成后,唤醒所有线程,让消费者消费
            System.out.println("生产完成,开始消费");
            Job.value.notifyAll();
        }
    }
}

 

  消费者代码:

/**
 * 消费者,等待生产者生成完商品后,才开始消费
 */
public class Consume implements Runnable {

    @Override
    public void run() {
        //注意,这里生产者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
        synchronized (Job.value) {
            //先判断有没商品生产,如果没有则开始等到生产者生成
            if (Job.value[0] == null) {
                try {
                    //等待生产
                    System.out.println("当前没有任务,等到生产");
                    Job.value.wait(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //生产完成后,唤醒消费者后面的线程任务,消费商品
            for (String string : Job.value) {
                System.out.println("消费"+string);
            }
        }

    }
}

  测试类:

public class Job {


    public static String value[]=new String[10] ;

    public static void main(String[] args) {
        
        Productor productor=new Productor();
        
        Consume consume=new Consume();
        
        new Thread(consume).start();
        new Thread(productor).start();

    }
}

  运行的结果:

二、ReentrantLock中的等待唤醒机制Condition类

  前面我们已经介绍了JDK中object自带的wait()与notify()方法,但是notify()在多线程环境下只能随机唤醒一个线程,这样的功能实在是有些鸡肋,为此在AQS(AbstractQueuedSynchronizer)中,为我们提供了一个Condition类实现了等待唤醒机制,并且Condition中唤醒的机制更加灵活。

  Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition。

  Condition中常用的是await()和signal()方法,与wait()与notify一样,必须获取同一把锁才能实现等待唤醒机制,接下来我们就使用Condition中await()和signal()方法对我们的生产者和消费者模式进行改造:再增加一个消费者2,如果商品队列为空,两个消费者都进行等待,当生产者生产完成后唤醒消费者1,消费者1消费完成后唤醒消费者2,这样就能够指定唤醒一个线程

  改造后的生成者:

/**
 * 生产者,生成了10个商品后唤醒所有线程
 */
public class Productor implements Runnable {
    //获取的锁
    Lock lock;
    //生成者和消费者1使用的等待/通知对象,用于绑定生成者和消费者1的等待/通知关系
    Condition condition;


    public Productor(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }

    @Override
    public void run() {
        //注意,这里生成者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
        lock.lock();
        int i = 0;
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        while (i < 5) {
            //生产任务
                Job.value[i] = "商品:" + i;
                System.out.println("开始生产" + Job.value[i]);
                i++;
        }
        //生产完成后,让消费者1消费
        System.out.println("生产完成,开始消费");
        condition.signal();
        //必须要释放锁
        lock.unlock();
    }
}

  改造后的消费者1:

/**
 * 消费者1,等待生产者生成完商品后,才开始消费
 */
public class Consume implements Runnable {
    //获取的锁
    Lock lock;
    //生成者和消费者1使用的等待/通知对象,用于绑定生成者和消费者1的等待/通知关系
    Condition condition;
    //消费者1和消费者2使用的等待/通知对象,用于绑定消费者1和消费者2的等待/通知关系
    Condition consumeCondition;

    public Consume(Lock lock, Condition condition, Condition consumeCondition) {
        this.lock = lock;
        this.condition = condition;
        this.consumeCondition = consumeCondition;
    }


    @Override
    public void run() {
        //注意,这里生产者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
        lock.lock();
            //先判断有没商品生产,如果没有则开始等到生产者生成
            if (Job.value[0] == null) {
                try {
                    //等待生产
                    System.out.println("我是消费者1当前没有任务,等待生产");
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //生产完成后,唤醒消费者后面的线程任务,消费商品
            for (String string : Job.value) {
                System.out.println("消费者1清理"+string);
            }
        System.out.println("消费者1清理完毕,唤醒消费者2进行消费");
        //当所有都消费完成后,通知消费者2进行消费
        consumeCondition.signal();
        //必须要释放锁
        lock.unlock();

    }
}

  新增消费者2:

/**
 * 消费者2,等待消费者1清理完成商品后,才开始消费
 */
public class Consume2 implements Runnable {
    //获取的锁
    Lock lock;
    //消费者1和消费者2使用的等待/通知对象,用于绑定消费者1和消费者2的等待/通知关系
    Condition consumeCondition;

    public Consume2(Lock lock, Condition consumeCondition) {
        this.lock = lock;
        this.consumeCondition = consumeCondition;
    }

    @Override
    public void run() {
        //注意,这里生产者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
        lock.lock();
            //先判断有没商品生产,如果没有则开始等到生产者生成
            if (Job.value[0] == null) {
                try {
                    //等待生产
                    System.out.println("我是消费者2,当前没有任务,等待生产");
                    consumeCondition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //生产完成后,唤醒消费者后面的线程任务,消费商品
            for (String string : Job.value) {
                System.out.println("消费者2消费"+string);
            }
        //必须要释放锁
        lock.unlock();

    }
}

  测试类:

public class Job {

    //创建一把锁
    private static Lock lock = new ReentrantLock();
    //生成者和消费者1使用的等待/通知对象,用于绑定生成者和消费者1的等待/通知关系
    private static Condition condition = lock.newCondition();
    //消费者1和消费者2使用的等待/通知对象,用于绑定消费者1和消费者2的等待/通知关系
    private static Condition consumeCondition = lock.newCondition();

    public static String value[] = new String[5];

    public static void main(String[] args) {

        Productor productor = new Productor(lock,condition);

        Consume consume = new Consume(lock,condition,consumeCondition);

        Consume2 consume2=new Consume2(lock,consumeCondition);
        new Thread(consume2).start();
        new Thread(consume).start();
        new Thread(productor).start();

    }
}

运行的结果:

 

posted @ 2019-09-24 15:44  想去天空的猫  阅读(167)  评论(0编辑  收藏  举报