前言
java提供了很多阻塞队列,在平时的开发中也会使用到,所以在此对java提供的阻塞队列进行一个了解总结
首先
java的阻塞队列都继承与BlockingQueue,其有共同的方法
boolean offer(Object o);//将数据o加入队列中,加入成功返回true,失败则为false,此方法不阻塞
boolean offer(Object o,long timeout,TimeUnit unit);//将o加入队列中,若timeout过后为未加入成功返回false,否则返回true,此方法会阻塞等待,unit为时间单位
put(Object o);//将数据o加入队列中,若队列没有空间则会阻塞当前的线程进行等待
Object poll();//取走队列头部的数据,如取不出则返回null
Object poll(long timeout,TimeUnit unit);//取走队列头部的数据,若取不出则等待timeout,等待后取不出返回null
Object take();//取出队列首部数据,若取不出则阻塞直到取出来
int drainTo(Collection c);//取出队列中所有数据放到容器c中,其中的排序为队列中的排序,返回取出的数量
int drainTo(Collection c,int maxLength);//取出队列的数据,最大数量为maxLength,返回实际获取得数量
其他队列基本都有实现以下的方法:
peek()与poll()功能一样
队列有add()方法,其内部实现与put()基本都是一样的.而且还有许多方法与列表方法一样用的,如size(),remove(),clear(),遍历时使用迭代器遍历.其中remove()为删除队头
队列的有界无界表明其是否指定或限定队列大小
ArrayBlockingQueue
用数组实现的有界阻塞队列,按照先进先出的原则.
public class ArrayBlockingQueueTest {
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(5,true);//定义队列大小为5,队列为先进先出顺序,false为未指定顺序
ExecutorService executorService = Executors.newSingleThreadExecutor();//单线程的线程池
public ArrayBlockingQueueTest() {
for (int i = 0;i<6;i++){
put("数据:"+i);//放入数据
}
}
public void put(String data){
try {
arrayBlockingQueue.put(data);//向队列中放入数据
} catch (InterruptedException e) {
e.printStackTrace();
}
handle();//开启线程处理
}
public void handle(){
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("处理中...");
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String data = null;
try {
data = arrayBlockingQueue.poll(1, TimeUnit.SECONDS);//取出数据,1秒内取不出返回null
} catch (InterruptedException e) {
e.printStackTrace();
}
if ( data != null) {
System.out.println(data+"处理结束...");
}else {
System.out.println("无数据处理...");
}
}
});
}
}
LinkedBlockingQueue
基于链表的阻塞队列,按照先进先出,其加入队列与取出队列的线程使用独立的锁来控制同步,所以其有更高的并发效率,需要注意的是在初始化时如果不指定长度会默认为无限长,这有可能会占用较多的资源,使用方法与ArrayBlockingQueue一致.
PriorityBlockingQueue
无界优先级队列,默认为升序,与Arrays.sort()方法排序类似,在初始化时可以指定其初始长度,默认为11,增长数量为当大于等于64长度时,为原长度的1.5被,当小于64的时候为原长度的2倍+2.初始化时可设置其排序的比较规则Comparator(),也可重写其compareTo方法.其无法排序同优先级
public class PriorityBlockingQueueTest {
PriorityBlockingQueue<String> priorityBlockingQueue;
String[] strings;
Comparator<String> mComparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.length() - o1.length();
}
@Override
public boolean equals(Object obj) {
return false;
}
};
public PriorityBlockingQueueTest() {
strings = new String[]{"666", "6626", "6645457645661234566", "6612423564566", "6644564564564564564564566", "664566", "664564566", "664566"};
priorityBlockingQueue = new PriorityBlockingQueue<>(11,mComparator);
priorityBlockingQueue.put("666");
priorityBlockingQueue.put("6626");
priorityBlockingQueue.put("6645457645661234566");
priorityBlockingQueue.put("6612423564566");
priorityBlockingQueue.put("6644564564564564564564566");
priorityBlockingQueue.put("664566");
priorityBlockingQueue.put("664564566");
priorityBlockingQueue.put("664566");
Iterator<String> iterator = priorityBlockingQueue.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("-----------------------------------------");
Arrays.sort(strings,mComparator);
for (String data : strings){
System.out.println(data);
}
}
}
运行结果
经试验验证,其内部的排序使用二分法排序,如下代码,结果与想象的不一样,具体原因还有待研究
private static <T> void siftUpUsingComparator(int k, T x, Object[] array,
Comparator<? super T> cmp) {
while (k > 0) {
int parent = (k - 1) >>> 1;//取中间值比较
Object e = array[parent];
if (cmp.compare(x, (T) e) >= 0)//此处遇到返回大于等于0的就退出,退出操作有点问题
break;
array[k] = e;
k = parent;
}
array[k] = x;
}
DelayQueue
延时取出的无界队列,基于PriorityQueue实现的,加入的元素需要实现Delayed接口,目前并不会使用
SynchronousQueue
不存储元素的队列,当插入的一个元素,必须等待其他线程移除才可继续运行,当移除一个元素,如使用remove且无插入在等待会报错,使用take()会等到有元素插入了才返回,否则阻塞,其他的有就有没有就返回null.
public class SynchronoutsQueueTest {
SynchronousQueue<String> stringSynchronousQueue = new SynchronousQueue<>();
public SynchronoutsQueueTest() {
new MyThread().start();
try {
stringSynchronousQueue.put("666");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----------putEndMain----------");
try {
System.out.println("----------poll:"+stringSynchronousQueue.take()+"Main----------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
class MyThread extends Thread{
@Override
public void run() {
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----------poll:"+stringSynchronousQueue.poll()+"Thread----------");
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
stringSynchronousQueue.put("666");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----------putEndThread----------");
}
}
}
运行结果
LinkedTransferQueue
无界的链表队列,用于生产者等待消费者的需求,其实现了TransferQueue接口,其中有几个重要方法
transfer(Object o);//加入队列,如加入时没有取出队列操作,会阻塞等待取出.
boolean tryTransfer(Object o);//加入队列,如有取出的操作在等待则加入并返回true,否则不加入并返回false
boolean tryTransfer(Object o,long timeout,TimeUnit unit);//有超时机制的tryTransfer
public class LinkedTransferQueueTest {
LinkedTransferQueue<String> linkedTransferQueue = new LinkedTransferQueue<>();
public LinkedTransferQueueTest() {
new TestThread().start();
while (true) {
try {
Thread.sleep(3000);
System.out.println("消费:" + linkedTransferQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class TestThread extends Thread{
@Override
public void run() {
while (true) {
try {
System.out.println("出产...");
linkedTransferQueue.transfer("34636");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
运行结果
在每次出产后都会等待消费后再进行下一次的出产
LinedBlockingDeque
双向阻塞队列,在初始化时可设置其最大容量,默认为int的最大值,队列提供了addFirst、addLast、offerFirst、offerLast、peekFirst、peekLast等方法进行对队头和队尾的操作。
小结
java提供的阻塞队列使用起来也是挺便利的,虽然有些可能写错了,希望大家指出纠正.