1.需求(为什么需要线程通信)
当我们需要多个线程完成同一任务时,并且希望他们有规律的执行,那么多线程之间需要一些通信机制,并且可以协调他们的工作,以此实现多个线程共同操作共享数据.
例 : A做包子,B吃包子,包子相当于共享操作的数据,B必须等到A做好才能吃,那么线程AB间就需要通信.即等待唤醒机制.
2.等待唤醒机制
这是多线程的一种协同的机制.谈到线程我们常常想到是多个线程竞争一把锁.但不完全是这样.线程之间也可以实现协同.
在一个线程满足某个条件时,就进入等待状态(wait),等待其他线程进行到某处指定代码时,将等待状态的线程唤醒(notify).可以指定wait的时间,过了这个时间可以自动唤醒@也可以使用notifyAll来唤醒所有等待的线程.
- wait : 线程不在参与活动,将释放CPU调度.进入wait set中,不会浪费CPU资源.也不会去竞争锁.此时线程状态为WAITING或者是TIME_WAITING.他等待其他线程执行某个操作(notify/wait(time)执行了time的时间),才能够将其从wait set中释放出来,重新参与CPU调度.
- notify : 选取待在wait set中的线程将其释放重新参与CPU调度.
- notifyAll : 将全部待在wait set中的线程全部释放参与CPU调度.
3.例 : 交替打印出输出的一百以内的数.
解释 : 调用start方法启动线程.假设是线程1(当然也可能是线程2)进入while语句并握住锁,进行输出语句后,调用wait()释放锁,线程2握住锁,进行到notify()唤醒线程1重新参与CPU调度......
public class WaitNotifyTest { public static void main(String[] args) { WaitNotify w = new WaitNotify(); Thread t1 = new Thread(w, "线程-1"); Thread t2 = new Thread(w, "线程-2"); t1.start(); t2.start(); } } class WaitNotify implements Runnable { int change = 100; Object obj = new Object(); @Override public void run() { while (true) { synchronized(obj) { obj.notify(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (change > 0) { System.out.println(Thread.currentThread().getName() + "\t" + change); change--; } try{ obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } 控制台 线程-1 100 线程-2 99 线程-1 98 线程-2 97 线程-1 96 线程-2 95 线程-1 94
注 :
- 这三个方法的使用必须都要在同步代码块或者同步方法中.
- 此三个方法的调用者必须是同步监视器,否则会触发异常.
- 按住Ctrl键,可以查看该三个方法的源码源码.
public final native void notify(); public final void wait() throws InterruptedException { wait(0L); }
此三个方法都声明在Object类中,Ctrl+F12,查看Object类.
- 因为同步监视器可以是任何对象(保证线程安全性需要满足唯一),由于该三个方法是Object类中的方法,所以同步监视器可以调用.
4.wait()与sleep()的区别.
(1). 查看源码 :
public static native void sleep(long millis) throws InterruptedException;
public final void wait() throws InterruptedException { wait(0L); }
(2) 相同点 :
一旦执行,都可以阻塞线程.
(3) 不同点 :
- 声明的位置不同 : sleep()方法声明在Thread类中,而且是静态的方法.wait()方法声明在Object类中.
- 使用的场景不同 : sleep()可以声明在任何所需要的场景.wait()必须声明在同步代码块或同步方法中(因为该写方法必须通过同步监视器调用).
- CPU调度 : 使用sleep()并不会释放CPU调度,只是线程阻塞.而wait()阻塞线程,并且释放该线程的CPU调度.
- 结束阻塞方式 : sleep(time)方法在时间time之后结束阻塞.wait(time)方法在时间time之后或者notify/notifyAll结束阻塞.