线程通信
如果使用synchronized关键词,实现线程同步,可以使用Object类提供的三个方法,这三个方法必须使用同步监视器来调用,对于同步方法,可以直接使用,因为该类默认实例this就是同步监视器,对于同步代码块,必须使用同步监视器对象来调用这三个方法。
- wait():导致当前线程等待,直到其他线程调用该同步监视器notify()方法或者notifyAll()方法来唤醒该线程。该方法有三种形式:无参,带毫秒参数,带毫秒和毫微妙参数。调用wait方法的当前线程会释放对同步监视器的锁定
- notify():唤醒在此同步监视器等待的单个线程,如果很多线程都在此同步监视器上等待,就唤醒其中一个。选择是任意的,只有当前线程放弃对该同步监视器锁定后,才可以执行被唤醒的线程
- notifyAll():唤醒在此同步监视器上等待的所有线程,只有当前线程放弃对该同步监视器锁定后,才可以执行被唤醒的线程
类似操作系统中的PV操作,阻塞线程和唤醒线程。
例子:
1 |
|
使用Condition控制线程通信
如果程序不使用synchronized关键词保证同步,而是直接使用Lock对象,那么就没有隐式同步监视器,就不能使用wait、notify、notifyAll方法进行线程通信。
当使用Lock对象来保证同步时,Java提供了一个Condition类来保持协调,使用Condition可以让那些已经得到Lock对象却无法继续执行的线程释放Lock对象,Condition对象也可以唤醒其他处于等待的线程。
Condition实例被绑定在一个Lock对象上,要获得Lock实例的Condition实例,可以调用Lock对象的newCondition方法,Condition类提供了三个方法:
- await():类似隐式同步监视器的wait方法,可以导致当前线程等待,直到其他线程调用该Condition的signal方法或signalAll方法来唤醒该线程,该await方法有多个变体,如long awaitNanos(long nanosTimeout)、void awaitUninterruptibly()、awaitUntil(Date deadline)可以完成更丰富的操作
- signal():唤醒在此Lock对象上等待的单个线程,如果有多个线程在等待,那么选择任意一个线程来唤醒。只有当前线程放弃对Lock对象的锁定后(使用await方法),才可以执行被唤醒的线程
- signalALL():唤醒在此Lock对象上等待的所有线程,只有当前线程放弃对Lock对象的锁定后(使用await方法),才可以执行被唤醒的线程
注意!!!!!:无论是使用wait方法还是await方法使线程阻塞后,再次唤醒线程后,会从上次阻塞的位置继续开始执行,而不是重新进入同步代码块.
使用阻塞队列BlockingQueue控制线程通信
Java提供了一个BlockingQueue接口,它虽然是Queue的子接口,但它的主要作用并不是作为容器,而是作为线程同步的工具,BlockingQueue具有一个特征:当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程被阻塞;当消费者线程试图从BlockingQueue中取出元素时,如果该队列已空,则该线程被阻塞。
程序的两个线程通过交替向BlockingQueue中放入、取出元素,可以很好的控制线程通信。
BlockingQueue提供了两个支持阻塞的方法:
- put(E e):尝试把E元素放入BlockingQueue中,如果该队列元素已满,则阻塞该线程
- take():尝试从BlockingQueue的头部取出元素,如果该队列的元素已空,则阻塞该线程
BlockingQueue继承自Queue,当然也可以使用Queue的方法,这些方法归纳起来可分为三组
- 在队列尾部插入元素,包括add(E e)、offer(E e)、put(E e)方法,当该队列已满时,这三个方法分别为抛出异常、返回false、阻塞队列
- 在队列头部删除元素并返回删除的元素,包括remove()、poll()、take()方法,当队列为空,这三个方法分别会抛出异常,返回false、阻塞队列
- 在队列头部取出但不删除元素,包括element()、peek()方法,当队列已空时,这两个方法分别抛出异常、返回false
BlockingQueue包括五个实现类
- ArrayBlockingQueue:基于数组实现的BlockingQueue队列
- LinkedBlockingQueue:基于链表实现的BlockingQueue队列
- PriorityBlockingQueue:它不是标准的阻塞队列,该队列调用remove、poll、take方法取出元素时,并不是取出队列中存在时间最长的,而是取出队列中最小的元素,PriorityBlockingQueue判断元素的大小可以根据元素本身大小来自然排序(实现Comparable接口),也可以使用Conparator接口进行定制排序
- SynchronizedQueue:同步队列,对该队列的存、取操作必须交替进行
- DelayQueue:是一个特殊的阻塞队列,底层基于PriorityBlockingQueue实现,不过Delay要求集合元素都实现Delay接口(该接口只有一个long getDelay()方法),DelayQueue根据集合元素的getDelay的返回值进行排序