概述
SynchronousQueue是一个特别的队列,在它的内部没有容器。其中每个插入操作必须等待另一个线程相应的删除操作,反之亦然。一个添加线程当它调用添加(put())方法时,如果没有其他线程尝试删除元素,此插入线程必须阻塞,等待删除线程调用删除操作,删除操作将会唤醒插入线程,同时删除线程会获取插入线程的数据。
验证代码如下:
public class SynchronousQueueDemo {
public static void main(String[] args) throws InterruptedException {
final SynchronousQueue<Integer> queue = new SynchronousQueue<>();
Thread putThread = new Thread(() -> {
System.out.println("put thread start");
try {
queue.put(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("put thread end");
});
Thread takeThread = new Thread(() -> {
System.out.println("take thread start");
try {
System.out.println("take from : " + queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("take from : " + poll);
System.out.println("take thread end");
});
putThread.start();
Thread.sleep(3000);
takeThread.start();
}
}
打印结果如下:
从结果中可以看出,put线程执行queue.put(1)
后线程阻塞,直到take线程执行queue.take()
后被唤醒。
put(E e):向队列提交一个元素,阻塞直到其他线程take()或poll()。
boolean offer(E e):向队列提交一个元素,如果此时有其他线程正在被take()阻塞(即其他线程已准备接收数据)或者碰巧有poll()操作,那么返回true,否则返回false。
E take():已有put()阻塞线程时,删除并返回put线程的元素;否则阻塞当前线程直至有其他线程put()或offer()时,删除并返回添加的元素。
E poll():如果此时已有线程被put()阻塞或者碰巧有offer()操作,则返回并删除该元素;否则返回null。
boolean add():其内部调用offer()方法,只是当offer()返回值为false时抛出IllegalStateException
异常。
E remove():内部调用poll()方法,只是当poll()返回值为null时抛出NoSuchElementException
异常。
SynchronousQueue调用peek()方法永远返回null,源码如下(jdk1.8):
public E peek() {
return null;
}
而调用element()方法时永远抛出NoSuchElementException,SynchronousQueue中的element()方法继承于AbstractQueue抽象类,且未重写,其源码如下(jdk1.8):
public E element() {
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
实现原理
SynchronousQueue实现策略通常分为公平模式和非公平模式,公平模式底层实现使用TransferQueue内部队列,非公平模式底层实现使用TransferStack内部栈。源码如下:
// 添加方法
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
if (transferer.transfer(e, false, 0) == null) {
Thread.interrupted();
throw new InterruptedException();
}
}
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) throw new NullPointerException();
if (transferer.transfer(e, true, unit.toNanos(timeout)) != null)
return true;
if (!Thread.interrupted())
return false;
throw new InterruptedException();
}
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
return transferer.transfer(e, true, 0) != null;
}
// 删除方法
public E take() throws InterruptedException {
E e = transferer.transfer(null, false, 0);
if (e != null)
return e;
Thread.interrupted();
throw new InterruptedException();
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E e = transferer.transfer(null, true, unit.toNanos(timeout));
if (e != null || !Thread.interrupted())
return e;
throw new InterruptedException();
}
public E poll() {
return transferer.transfer(null, true, 0);
}
从以上代码可以看出无论时添加还是删除元素都是调用transferer
对象的transfer()
方法。
private transient volatile Transferer<E> transferer;
public SynchronousQueue() {
this(false);
}
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
从其构造函数可以证明上面的结论,且默认为非公平模式。公平模式下先进先出,非公平模式下先进后出。