多线程 4——线程通信、线程池(更新中。。。)

一、线程通信

1、等待集

对于java中的每一个对象:

  • 1)对象上关联着一个monitor lock(监视器锁)
  • 2)对象上还关联着一个数据结构,即:等待集(Wait Set)———保存的所有调用过这个对象.wait()方法的线程。

线程通信这引入了对象的等待集,是三个非常重要的方法:

  • wait():让当前线程进入等待状态
  • notify():唤醒当前对象上的等待的单个线程
  • notifyAll():唤醒当前对象上的等待的所有线程

这三个方法都是Object类的方法。另外,我们需要知道一个数据结构——等待集(Wait Set).

2、wait()方法

使用注意:

wait()方法必须用在synchronized代码段里。 如果调用wait()时,没有持有适当的锁,会抛出异常,如下图所示:
多线程 4——线程通信、线程池(更新中。。。)

当一个对象(对象o)调用了wait()方法(即:o.wait() ),会产生什么结果呢?即:

wait()方法的作用:

  • 当前线程进入等待状态(阻塞等待,放弃抢占CPU的资格),线程状态由RUNNABLE ----> WAITING;会阻塞在此行代码。

  • 当前线程放弃所持有的对象锁如果没有对对象加锁,直接使用wait方法释放锁就会抛异常,如果加锁的对象找不到或者不存在也是会抛异常的.

  • 唤醒同步代码块所在的线程(即:synchronized加锁的线程,因为调用wait()方法会释放对象锁),但不能唤醒被wait()方法阻塞的线程

  • 如何被唤醒:只能通过别的线程调用notify()或notifyAll()来唤醒

3、notify() / notifyAll()方法

使用注意:

notify() / notifyAll()方法也是使用在synchronized代码块里

wait()方法的作用:

1> 唤醒被wait()方法阻塞的线程

  • notify() :是随机唤醒一个被wait()方法阻塞的线程;
  • notifyAll()是唤醒一个被wait()方法阻塞的全部线程

2> 唤醒时间:在当前线程的synchronied代码块执行完唤醒被wait()方法阻塞的线程。(一定要注意:不是一调用notify() / notifyAll()方法就可以l立即唤醒其他线程

4、等待队列/同步队列

让线程等待一共有三个状态:阻塞(BLOCKED)、等待(WAITING)、超时等待(TIME_WAITING)
多线程 4——线程通信、线程池(更新中。。。)

阻塞态(BLOCKED)

1)产生阻塞态(BLOCKED)的条件: 同步代码块(synchronized)所在线程竞争锁失败
2)同步队列: 支持多线程操作,并且是线程安全的;

  • 作用:
    • synchronized导致线程竞争对象锁失败的线程(运行态-->阻塞态)全部会被JVM放到同步队列里;
    • 竞争锁成功的线程释放锁之后,JVM会把竞争同一对象锁失败的线程全部唤醒,让他们再次竞争,竞争失败的就又放回同步队列,依次下去。。。
  • 效率: 比较低,因为需要在阻塞态和唤醒态之间来回切换

等待(WAITING)、超时等待(TIME_WAITING)

1)产生这两种状态的条件:

  • 等待(WAITING):wait()、join()
  • 超时等待(TIME_WAITING):wait()、join()、sleep()

2)等待队列:

  • 作用: 出现等待(WAITING)、超时等待(TIME_WAITING)这两种状态的线程都会被JVM放到等待队列。 只有满足一定条件才能被唤醒,并向下执行代码。

    • join():等待调用join()方法的线程执行完 才能从等待队列中唤醒当前线程,并让当前线程向下执行。
    • wait():等待其他线程通过notify()/notifyAll()方法调用才能从等待队列中唤醒。
    • sleep():到达休眠的时间才能从等待队列中唤醒。
  • 效率: 比较高,因为只用一个状态的转变,即等待/超时等待状态被唤醒转变为就绪态,不需要在不同状态之间来回切换.