阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作时:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

 特点:

  • 当阻塞队列为空时,从队列中获取元素的操作将会被阻塞。
  • 当阻塞队列是满时,往队列里添加元素的操作将会被阻塞。

阻塞在多线程中是指在某些情况下会挂起线程,一旦条件成熟,被阻塞的线程就会自动唤醒。而阻塞队列和唤醒队列是由阻塞队列控制。

试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列里插入新的元素;同样,试图往已满的阻塞队列中添加新元素的线程也会阻塞。直到其他线程从队列中移除一个或多个元素或清空队列后使队列重新空闲起来并后续新增。

阻塞队列的种类:

  • ArrayBlockingQueue:由数组结构组成的有界阻塞队列
  • LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列
  • PriorityBlockingQueue:支持优先级排序的无界阻塞队列
  • DelayQueue:使用优先级队列实现的延迟无界阻塞队列
  • SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列
  • LinkedTransferQueue:由链表结构组成的无界阻塞队列
  • LinkedBlockingDeque:由链表组成的双向阻塞队列

BlockingQueue的核心方法

 1)抛出异常:

    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        //add方法添加元素,返回boolean。当阻塞队列满时,再往阻塞队列add插入元素会抛 java.lang.IllegalStateException: Queue full
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
     // System.out.println(blockingQueue.add("d"));

        //element方法返回队列的第一个元素,当阻塞队列为空时,抛出java.util.NoSuchElementException
        System.out.println(blockingQueue.element());
        
        //remove方法清空队列第一个元素,并返回该元素。当阻塞为空时,抛出java.util.NoSuchElementException
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());

       // System.out.println(blockingQueue.element());
        System.out.println(blockingQueue.remove());
    }
  • add方法添加元素到阻塞队列。当阻塞队列满时,再往队列里add插入元素会抛出java.lang.IllegalStateException: Queue full
  • remove方法清除阻塞队列的第一个元素。当阻塞队列空时,再从队列里移除元素会抛出java.util.NoSuchElementException
  • element方法获取阻塞队列的第一个元素。当阻塞队列空时,再从队列里获取元素会抛出java.util.NoSuchElementException

2)特殊值

       //特殊值
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        
        //offer方法添加元素,添加成功返回true,添加失败就返回false
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("d"));

        //peek方法获取队列里的第一个元素,如果队列为空,则返回null
        System.out.println(blockingQueue.peek());

        //poll方法移除队列里的第一个元素,并返回该元素,如果队列为空,则返回null
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
  • offer方法添加元素,添加成功返回true,添加失败就返回false
  • peek方法获取队列里的第一个元素,如果队列为空,则返回null
  • poll方法移除队列里的第一个元素,并返回该元素,如果队列为空,则返回null

3)一直阻塞

        //一直阻塞
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        //put方法添加元素,当阻塞队列满时,再往队列里添加元素,队列会一直阻塞线程直到有其他线程take数据或响应中断退出
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
       // blockingQueue.put("d");

        //take方法移除元素,当阻塞队列空时,再从队列里移除元素,队列会一直阻塞线程直到队列可用
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
      //  System.out.println(blockingQueue.take());
  • put方法添加元素。当阻塞队列满时,再往队列里添加元素,队列会一直阻塞线程直到有其他线程take数据或响应中断退出
  • take方法移除元素。当阻塞队列空时,再从队列里移除元素,队列会一直阻塞线程直到队列可用

4)超时退出

        //超时退出
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        //offer方法添加元素,并返回boolean,如果队列为满,阻塞一定时间,然后退出
        System.out.println(blockingQueue.offer("a", 2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.offer("b", 2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.offer("c", 2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.offer("d", 2L, TimeUnit.SECONDS));

        //poll获取队列里第一个元素,如果队列为空时,阻塞一定时间,然后退出
        System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));
  • offer方法添加元素,并返回boolean,如果队列为满时,阻塞一定时间,然后退出
  • poll获取队列里第一个元素,如果队列为空时,阻塞一定时间,然后退出

SynchronousQueue

SynchronousQueue没有容量。与其他BlockingQueue不同,SynchronousQueue是一个不存储元素的BlockingQueue。每一个put元素操作都必须要等待一个take操作,否则不能继续添加元素,反之亦然。

public class SynchronousQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();


        new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + "\t put 1");
                blockingQueue.put("1");

                System.out.println(Thread.currentThread().getName() + "\t put 2");
                blockingQueue.put("2");

                System.out.println(Thread.currentThread().getName() + "\t put 3");
                blockingQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }, "t1").start();


        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
                
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
                
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2").start();
    }
}

结果:

 

 posted on 2020-12-09 21:13  会飞的金鱼  阅读(1926)  评论(0)    收藏  举报