多线程 4——线程通信、线程池(更新中。。。)
一、线程通信
1、等待集
对于java中的每一个对象:
- 1)对象上关联着一个monitor lock(监视器锁);
- 2)对象上还关联着一个数据结构,即:等待集(Wait Set)———保存的所有调用过这个对象.wait()方法的线程。
线程通信这引入了对象的等待集,是三个非常重要的方法:
- wait():让当前线程进入等待状态
- notify():唤醒当前对象上的等待的单个线程
- notifyAll():唤醒当前对象上的等待的所有线程
这三个方法都是Object类的方法。另外,我们需要知道一个数据结构——等待集(Wait Set).
2、wait()方法
使用注意:
wait()方法必须用在synchronized代码段里。 如果调用wait()时,没有持有适当的锁,会抛出异常,如下图所示:
当一个对象(对象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)
阻塞态(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():到达休眠的时间才能从等待队列中唤醒。
-
效率: 比较高,因为只用一个状态的转变,即等待/超时等待状态被唤醒转变为就绪态,不需要在不同状态之间来回切换.