[大白装逼]java阻塞队列

前言

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提供的阻塞队列使用起来也是挺便利的,虽然有些可能写错了,希翼大家指出纠正.

个人博客:https://lewis-v.github.io/

公众号
个人公众号

</article>

推荐阅读更多精彩内容

  • 相关文章Java并发编程(一)线程定义、状态和属性 Java并发编程(二)同步Java并发编程(三)volatil...
    刘望舒阅读 4,011评论 1赞 31
  • 1.阻塞队列定义阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。...
    SDY_0656阅读 87评论 0赞 1
  • 编辑: 一字马胡 转载标志 【2017-11-03】 更新日志 阻塞队列 阻塞队列是一种队列,一种可以在多线程环...
    一字马胡阅读 14,578评论 6赞 38
  • 1.如何暂停或恢复线程 在JDK中提供了以下两个方法(类Thread)用来暂停线程和恢复线程。 ?suspend方...
    pgl2011阅读 716评论 0赞 0
  • 001追求 大家要么在追求的路上,要么在寻找的路上。不管怎样都要记得:追求自己在意的,做自己想做的事情。 002岁...
    思彤lu阅读 33评论 0赞 2