一直搞不懂Java线程通信,这次终于明白了( 四 )


一直搞不懂Java线程通信,这次终于明白了

文章插图
总结:
  • notify方法很容易引起死锁,除非你根据自己的程序设计,确定不会发生死锁,notifyAll方法则是线程的安全唤醒方法
  • 如果程序允许超时唤醒,则可以使用wait(long timeout)方法
  • wait(long timeout,int nanou):与 wait(long timeout)相同,不过提供了纳秒级别的更精确的超时控制
ReentrantLock结合ConditionCondition是JDK1.5新增的接口,在java.util.concurrent.locks 包中,提供了类似的Object的监视器方法,与Lock配合可以实现等待/通知模式,方法作用在下方源码中已简单注释,想要查看详细说明,强烈建议看源码,通过翻译软件翻译一下就行!
package java.util.concurrent.locks;import java.util.concurrent.TimeUnit;import java.util.Date;public interface Condition {//使当前线程在接到信号或被中断之前一直处于等待状态void await() throws InterruptedException;// 使当前线程在接到信号之前一直处于等待状态 。【注意:该方法对中断不敏感】 。void awaitUninterruptibly();// 使当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态 。// 返回值表示剩余时间,如果在nanosTimesout之前唤醒,那么返回值 = nanosTimeout - 消耗时间,如果返回值 <= 0 ,则可以认定它已经超时了long awaitNanos(long nanosTimeout) throws InterruptedException;// 使当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态boolean await(long time, TimeUnit unit) throws InterruptedException;// 使当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态 。如果没有到指定时间就被通知,则返回true,否则表示到了指定时间,返回返回falseboolean awaitUntil(Date deadline) throws InterruptedException;// 唤醒一个等待线程 。该线程从等待方法返回前必须获得与Condition相关的锁 。void signal();// 唤醒所有等待线程 。能够从等待方法返回的线程必须获得与Condition相关的锁void signalAll();}在此我们通过经典的生产者消费者案例说一下Condition实现线程通信,多几种案例思维更宽阔,多样化理解对技术刺激更大
案例:有一个快递点,可以接货和送货,最多存放5个包裹,再放就提示包裹已满,派件时包裹送完就不能再送,提示没有包裹,不能派送
快递点:
package com.stt.thread.communication;import java.util.concurrent.atomic.AtomicInteger;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;/** * 快递点: * goodsNumber: 快递数量,默认为0,最多5个,保障原子性使用 AtomicInteger* receiving() : 收货方法,累加货物数量,每次 + 1 * dispatch() : 派送方法,递减数量,每次 - 1 * 注意:因为使用 Condition 实现,Condition 需要通过 ReentrantLock 获取, *所以可以使用 ReentrantLock实现同步就不需要 synchronized */public class ExpressPoint {// 快递数量,使用原子类private AtomicInteger goodsNumber = new AtomicInteger();// 锁对象private ReentrantLock lock = new ReentrantLock();// 创建线程通信对象private Condition condition = lock.newCondition();// 收货方法,使用Lock锁,就不需要synchronized同步了public void receiving() {// 上锁lock.lock();// 写try...finally,保障无论是否发生异常都可以解锁,避免死锁try {// 如果达到5个,就提示,并且等待while (goodsNumber.get() == 5) {System.out.println("库房已满,已不能再接收!");// 等待,有异常抛出condition.await();}System.out.println(Thread.currentThread().getName() + "已收到编号:" + goodsNumber.incrementAndGet() + "的包裹");// 唤醒其他线程condition.signalAll();} catch (InterruptedException e) {throw new RuntimeException(e);} finally {// 解锁lock.unlock();}}// 派送方法public void dispatch() {// 上锁lock.lock();try {// 等于0就不能再派送while (goodsNumber.get() == 0) {System.out.println("没有包裹,不能派送!");condition.await();}System.out.println(Thread.currentThread().getName() + "已送出编号:" + goodsNumber.get() + "的包裹");goodsNumber.decrementAndGet();condition.signalAll();} catch (InterruptedException e) {throw new RuntimeException(e);} finally {// 解锁lock.unlock();}}}测试类:通过while死循环,不断接货和送货
public class ExpressPointMain {public static void main(String[] args) {ExpressPoint expressPoint = new ExpressPoint();// 收货线程new Thread(() -> {while (true){expressPoint.receiving();}},"收货员").start();// 送货线程new Thread(() -> {while (true){expressPoint.dispatch();}},"送货员").start();}}


推荐阅读