Condition的await和signal与Object的wait与notify区别:
任何一个Java对象都天然继承于Object类,在线程间实现通信会用到Object的几个方法,如wait(),wait(long timeout),wait(long timeout,int nanos)与notify(),notifyAll()这几个方法实现等待通知机制,同样的,在Java Lock体系有同样的方法实现等待通知机制。从整体上看Object的wait与notify是与对象监视器(synchronized同步代码块或者同步方法中)配合完成线程间的等待通知机制,而Condition的await和signal与Lock配合完成等待通知机制,前者是JVM底层级别(不可以看源码),后者是Java语言级别,具有更高的可控制性和扩展性(可以看源码)。
两者在功能特性上还有如下不同:
1.Condition支持不响应中断,而Object不支持,也就是Object只要有中断就要响应。
2.Condition支持多个等待队列(new多个Condition对象),而Object只有一个等待队列,但两者都只要一个同步队列;
3.Condition支持截止时间设置,而Object是超时时间设置,支持截止时间设置,不用计算需要等多久。
Condition :
public interface Lock {
Condition newCondition();
}
public interface Condition {
//当前线程进入等待状态,直到被中断或唤醒
void await() throws InterruptedException;
//不响应中断(即使有中断,也不会抛异常),直到被唤醒
void awaitUninterruptibly();
//当前线程进入等待状态直到被通知,中断或者超时;
long awaitNanos(long nanosTimeout) throws InterruptedException;
//同Object.wait(long timeout),多了自定义时间单位
//直到超时,中断,被唤醒
boolean await(long time, TimeUnit unit) throws InterruptedException;
//支持设置截止时间,直到到截止时间、中断、被唤醒
boolean awaitUntil(Date deadline) throws InterruptedException;
//唤醒一个等待在Condition(等待队列)上的线程,将该线程由等待队列转移到同步队列
void signal();
//将所有等待在condition上的线程全部转移到同步队列中
void signalAll();
Condition实现原理分析:
等待队列
创建一个Condition对象是通过lock.newCondition( ),而这个方法实际上会new出一个ConditionObject对象,该类是AQS的一个内部类(lock的实现原理依赖AQS)。
public class ConditionObject implements Condition, java.io.Serializable {
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
}
在ConditionObject 通过持有等待队列的头尾指针来管理等待队列。这个Node复用了AQS的Node类,也就是等待队列和同步队列的结点一个Node类。
Node类中有这样一个属性:
//后继节点
Node nextWaiter;
nextWaiter是等待队列中标识下一个结点,也就是说Node结点的prev和next等待队列没有用到。
等待队列是一个单向队列,而同步队列是一个双向队列。
package CODE.多线程;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Await {
public static void main(String[] args) {
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
for(int i=0;i<10;i++)
{
Thread thread=new Thread(()->
{
lock.lock();
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
});
thread.start();
}
}
}
Debug模式下可以看见:
1.调用condition.wait方法后线程依次尾插到等待队列中,对上例来说,顺序依次是Thread-0,Thread-1,Thread-2…Thread-9;
2.等待你队列是一个单向队列,只有nextWaiter。
由于condition是new出来的,也就是说当多次调用lock.newCondition()可以创建多个condition对象,也就是一个lock可以有多个等待队列。
用Object类wait在Object对象监视器上只有一个同步队列和一个等待队列,而在并发包中的Lock中有一个同步队列多个等待队列。
Condition的应用:实现有界队列
有界队列是一种特殊的队列,当队列为空时,队列的获取(删除)操作将会阻塞获取(删除)线程,直到队列中有新增结点;当队列已满时,队列的插入操作将会阻塞添加线程,直到队列出现空位。
代码如下:
package CODE.多线程;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//Conditon实现有界队列
class BoundQueue<T>
{
private Object[] items;
private int counts=0; //intems中元素个数
private Lock lock=new ReentrantLock();
private Condition fullCondition=lock.newCondition();
private Condition emptyCondition=lock.newCondition();
public BoundQueue(int size)
{
items=new Object[size];
}
//向数组里添加元素,如果数组满,进入等待状态
public void add(T t,int addIndex) throws InterruptedException {
try
{