转载

Java并发编程那些事儿(四)——线程间的协作

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。

这是并发编程系列的第四篇文章。上一篇介绍的是通过 ThreadLocal 的方式实现多线程间的共享资源的访问,这篇介绍一下线程之间如何进行通信。

之前介绍的内容都是如何保证线程之间的运行互不干扰,但是有的时候,线程之间必须互相合作。比如清洗盘子完成之后,才能对盘子进行烘干操作,烘干必须在清洗之后,那么清洗线程和烘干线程如何进行沟通呢?

Java 进程间的通信与访问共享变量一样,都需要借助互斥的特性来实现,在互斥的基础上, JDK 为线程提供了一种自我挂起的能力。也就是说想实现进程间的通信,前提必须是在 synchronized 同步块或者方法中实现。

实现互斥同步机制, Java 提供了两种方法。内置锁 synchronized 和显示锁 Lock ,所以线程间通信也有两种方式。

第一种方式是基于内置锁,通过 Object 对象提供的 wait() 方法以及 notify()/notifyAll() 方法实现。

第二种方式是基于显示锁,通过 Condition 对象的 await() 方法和 signal()/signaAll() 方法。

内置锁——线程间通信

wait() :

如果当前线程在等待某个条件发生变化之后,才能继续执行,但是触发条件发生变化,超出了当前线程的能力。通常需要另外一个线程来改变这种条件的时候,就需要考虑使用 wait() 方法,将当前线程挂起,等待条件的改变。

比如想把盘子由清洗状态改为烘干状态,必须由烘干线程完成。只有烘干线程才知道烘干操作什么时候完成。

当调用 wait() 方法将当前挂起之后,只有在 notify() 或者 notifyAll() 方法发生时,这个任务才会被唤醒,继续执行。

wait() 方法提供了两种使用形式,第一种接受一个时间参数,表示在此时间范围内执行挂起操作,时间到期后自动恢复。第二种就是不接受时间参数,那么将无限挂起,除非调用了 notify() 或者 notifyAll() 方法。

notify()/notifyAll() :表示唤醒正在因调用了 wait() 方法而等待的线程。 notify() 只唤醒一个等待线程,而 notifyAll() 会唤醒所有因持有同一把锁而等待的任务。

wait()sleep() 的区别

wait()sleep() 最大的区别在于,调用 wait() 方法时,会释放锁资源,而 sleep() 方法则不会。

显示锁——线程间通信

基于显示锁实现的线程间同步机制是通过 Condition 对象的 await() 方法和 signal()/signalAll() 方法实现的。

await() 方法的作用等同于 wait()signal()/signalAll() 等同于 notify()/notifyAll() 方法。

示例一

通过示例代码演示一下文章开头描述的场景,一个洗碗线程,一个烘干线程,当碗是湿的时候,则挂起,同时唤醒烘干线程,反之亦然。

定义Dish

 1public class Dish {
 2    private boolean isWashing = true;
 3    //清洗
 4    public synchronized void washing() throws InterruptedException {
 5        //如果已经是清洗过的状态,则挂起
 6        while (isWashing == true){
 7            wait();
 8        }
 9        System.out.println("washing被唤醒,正在washing……");
10        Thread.sleep(500);
11        isWashing = true;
12        //通知其它进程,可以开始工作了。
13        notifyAll();
14    }
15    //烘干
16    public synchronized void drying() throws InterruptedException {
17        //如果是干的,则挂起
18        while (isWashing == false){
19            wait();
20        }
21        System.out.println("drying被唤醒,正在drying....");
22        Thread.sleep(500);
23        isWashing = false;
24        //通知其它线程
25        notifyAll();
26    }
27}

清洗任务

 1public class WashingTask implements Runnable {
 2    private Dish dish;
 3    public WashingTask(Dish dish){
 4        this.dish = dish;
 5    }
 6    @Override
 7    public void run() {
 8        //如果是湿,则挂起
 9        while (true){
10            try {
11                dish.washing();
12            } catch (InterruptedException e) {
13                e.printStackTrace();
14            }
15        }
16    }
17}

烘干任务

 1public class DryingTask implements Runnable {
 2    private Dish dish;
 3    public DryingTask(Dish dish) {
 4        this.dish = dish;
 5    }
 6    @Override
 7    public void run() {
 8        while (true){
 9            try {
10                dish.drying();
11            } catch (InterruptedException e) {
12                e.printStackTrace();
13            }
14        }
15    }
16}

测试程序

1public static void main(String[] args){
2    Dish dish = new Dish();
3
4    Thread washer = new Thread(new WashingTask(dish));
5    Thread dryer = new Thread(new DryingTask(dish));
6
7    washer.start();
8    dryer.start();
9}

该程序会交替输出如下结果

1drying被唤醒,正在drying....
2washing被唤醒,正在washing……

示例二

通过 Condition 对象的 awai()signal() 方法实现同样的功能。

通过显示锁实现的Dish

 1public class DishLock {
 2    private boolean isWashing = true;
 3    //显示锁
 4    private Lock lock = new ReentrantLock();
 5    //通过显示锁拿到Condition对象
 6    private Condition condition = lock.newCondition();
 7    public void washing() throws InterruptedException {
 8        lock.lock();
 9        try{
10            //如果已经是清洗过的状态,则挂起
11            while (isWashing == true){
12                condition.await();
13            }
14            System.out.println("washing被唤醒,正在washing……");
15            Thread.sleep(500);
16            isWashing = true;
17            //通知其它进程,可以开始工作了。
18            condition.signalAll();
19        }finally {
20            lock.unlock();
21        }
22    }
23
24    public void drying() throws InterruptedException {
25        lock.lock();
26        try{
27            //如果是干的,则挂起
28            while (isWashing == false){
29                condition.await();
30            }
31            System.out.println("drying被唤醒,正在drying....");
32            Thread.sleep(500);
33            isWashing = false;
34            //通知其它线程
35            condition.signalAll();
36        }finally {
37            lock.unlock();
38        }
39    }
40}

清洗任务

 1public class WashingTask implements Runnable {
 2    private DishLock dishLock;
 3    public WashingTask(DishLock dishLock) {
 4        this.dishLock = dishLock;
 5    }
 6    @Override
 7    public void run() {
 8        //如果是湿,则挂起
 9        while (true){
10            try {
11                dishLock.washing();
12            } catch (InterruptedException e) {
13                e.printStackTrace();
14            }
15        }
16    }
17}

烘干任务

 1public class DryingTask implements Runnable {
 2    private DishLock dishLock;
 3    public DryingTask(DishLock dishLock) {
 4        this.dishLock = dishLock;
 5    }
 6    @Override
 7    public void run() {
 8        while (true){
 9            try {
10                dishLock.drying();
11            } catch (InterruptedException e) {
12                e.printStackTrace();
13            }
14        }
15    }
16}

测试程序

1public static void main(String[] args){
2    DishLock dishLock = new DishLock();
3    Thread washer = new Thread(new WashingTask(dishLock));
4    Thread dryer = new Thread(new DryingTask(dishLock));
5
6    washer.start();
7    dryer.start();
8}

输出结果同上。

结束

这篇文章讲述了,线程之间如何协作,是典型的生产者消费者模型。下一篇会讲一下 Java 提供的三个并发编程工具类,闭锁,信号量和栅栏。

·END·

花括号MC

Java·大数据·个人成长

Java并发编程那些事儿(四)——线程间的协作

微信号:huakuohao-mc

点一下你会更好看耶

Java并发编程那些事儿(四)——线程间的协作

原文  http://mp.weixin.qq.com/s?__biz=MzUzMzE4MDY0Nw==&mid=2247483981&idx=1&sn=5f6cbf21e90d6010d008c9e2f1822e97
正文到此结束
Loading...