常见的线程之间通讯方式有以下几种:java
一、wait和notify/notifyAll并发
二、await和signal/signalAllide
三、sleep/yield/join工具
四、同步屏障CyclicBarrier学习
五、CountDownLatch 闭锁spa
六、Semaphore 信号量.net
注意:四、五、6参见后续章节线程
等待/通知机制,是指一个线程A调用了对象O的wait方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操做。上述两个线程经过对象O完成交互,而对象上的wait()和notify()/notifyAll()的关系就如同开关信号同样,用来完成等待方和通知方之间的交互工做。code
建立两个线程WaitThread和NotifyThread,前者检查flag值是否为false,若是符合要求,进行后续操做,不然在lock上等待,后者在睡眠了一段时间值对lock进行通知,示例代码以下:orm
package com.black.example.helloworld.thread; import ch.qos.logback.core.util.TimeUtil; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.TimeUnit; /** * 等待/通知 超时设置模式 */ public class WaitNotify { static boolean flag = true; static Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread waitThread =new Thread(new WaitDemo(),"WaitThread"); waitThread.start(); TimeUnit.SECONDS.sleep(1); Thread notifyThread =new Thread(new NotifyDemo(),"NotifyThread"); notifyThread.start(); } static class WaitDemo implements Runnable{ @Override public void run() { //加锁,拥有lock的monitor synchronized (lock){ //条件不知足时,继续wait,同时释放lock的锁 while (flag){ try { System.out.println(Thread.currentThread()+"flag is true, wait @ "+ new SimpleDateFormat("HH:mm:ss").format(new Date())); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //条件知足时,完成工做 System.out.println(Thread.currentThread() + " flag is false,running @ "+ new SimpleDateFormat("HH:mm:ss").format(new Date())); } } } static class NotifyDemo implements Runnable{ @Override public void run() { try { //加锁,拥有lock对象的Monitor synchronized (lock){ //获取lock的锁,而后进行通知,通知时不会释放lock的锁 //直到当前线程释放了lock后,WaitThread才能从wait方法中返回 System.out.println(Thread.currentThread()+" hold lock, notify @ " + new SimpleDateFormat("HH:mm:ss").format(new Date())); lock.notifyAll(); flag = false; TimeUnit.SECONDS.sleep(5); } //再次加锁 synchronized (lock){ System.out.println(Thread.currentThread()+" hold lock again, notify @ " + new SimpleDateFormat("HH:mm:ss").format(new Date())); TimeUnit.SECONDS.sleep(5); } } catch (InterruptedException e) { e.printStackTrace(); } } } }运行结果以下:
Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,须要提早获取Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)建立出来的,换句话说,Condition是依赖Lock对象的。
通常都会讲Condition对象做为成员变量。当调用await()方法后,当前线程会是否锁并进入等待,并且其余线程调用signal()方法,通知当前线程后,当前线程才从await()方法返回,而且在返回前已经获取到了锁。
经常使用方法及描述,见下表:
await() 等待 与 singnal()通知的使用,示例以下:
package com.black.example.mutileThread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Condition 配合Lock 实现线程的等待 与通知 * Created by liuzp on 2018/7/24. */ public class ConditionDemo { public static Lock lock = new ReentrantLock(); public static Condition condition = lock.newCondition(); public static void main(String[] args) { new Thread() { @Override public void run() { lock.lock();//请求锁 Thread.currentThread().setName("await-thread-lzp"); try { System.out.println(Thread.currentThread().getName() + "==》进入等待状态,直到被通知"); condition.await();//设置当前线程进入等待 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock();//释放锁 } System.out.println(Thread.currentThread().getName() + "==》继续执行"); } }.start(); new Thread() { @Override public void run() { lock.lock();//请求锁 Thread.currentThread().setName("signal-thread-lzp"); try { System.out.println(Thread.currentThread().getName() + "=》进入唤醒一个Condition上的线程"); Thread.sleep(2000);//休息2秒 condition.signal();//随机唤醒等待队列中的一个线程 System.out.println(Thread.currentThread().getName() + "休息结束"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock();//释放锁 } } }.start(); } }运行结果:
- 对于sleep()方法应该很熟悉了,让当前线程睡眠一段时间。期间不会释听任何持有的锁。
- yield()方法其做用主要是让当前线程从运行状态转变为就绪状态,由线程调度从新选择就绪状态的线程分配CPU资源。至于最终会选取哪一个线程分配CPU资源就由调度策略来决定了,有可能仍是该线程,有可能换为其它线程。
- join方法,做用是暂停当前线程,等待被调用线程指向结束以后再继续执行。
使用join的时候须要注意:
一、调用join的时候,当前线程不会释放掉锁,若是调用线程也须要该锁则就会致使死锁!
二、join方法不会启动调用线程,因此,在调用join以前,该调用线程必须已经start启动,不然不会达到想要的效果。
join的底层实际是就是使用了一个自旋等待机制,判断调用线程是否死亡,若是没有则一直让当前线程wait。能够看一下底层实现源码:public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) {//若是调用者依旧没有结束,让当前线程进行等待 wait(0);//注意这里的wait是等待的当前线程,而不是调用者线程 } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay);//指定等待的时间 now = System.currentTimeMillis() - base; } } }
上一篇:学习笔记四:初识线程