原创:花括号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·大数据·个人成长
微信号:huakuohao-mc
点一下你会更好看耶