谈谈阻塞队列以及阻塞队列四种方法类型

    在多线程领域,所谓的阻塞,在某些情况下会挂起线程即阻塞,一旦满足某条件时,被挂起的线程又会自动被唤醒。队列的数据结构大家并不陌生,先进先出,先到先得,什么是阻塞队列呢,顾名思义,首先它是一个队列,当阻塞队列是空时,从队列中获取元素的操作将会被阻塞,当阻塞队列是满时,往队列里添加元素的操作将会被阻塞,那为什么需要阻塞队列呢,好处就是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,,在concurrent包发布之前,线程的控制,需要程序员自己来通过synchronized来控制线程的阻塞(wait)和唤醒(notify,notifyAll),当concurrent包发布之后这一切阻塞线程都为我们一手包办了,那阻塞队列有哪些种类呢。

  • ArrayBlockingQueue 是一个基于数组的有界阻塞队列,次队列按FIFO(先进先出)原则对元素进行排序
  • LinkedBlockingQueue 是一个基于链表结构的有界(默认值是Integer.MAX_VALUE)阻塞队列,次队列按FIFO(先进先出)排序元素,吞吐量通常要高于ArrayBlockQueue
  • SynchronousQuere是一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于前两种
  • PriorityBlockingQueue 支持优先级排序的无界阻塞队列
  • DelayQueue 使用优先级队列实现的延迟无界阻塞队列
  • LinkedTransferQueue 有链表结构组成的无界阻塞队列
  • LinkedBlockingDeque 有链表结构组成的双向阻塞队列

     以后的文章我们着重讲解ArrayBlockingQueue,LinkedBlockingQueue和SynchronousQuere,因为后边说到线程池的时候主要是这三个。

    接下来,我们按照下边的表来一个一个介绍阻塞线程的方法类型。

方法类型 抛出异常 特殊值 阻塞 超时
插入 add offer put offer
移除 remove poll take poll
检查

element

peek 不可用 不可用

    先来看,抛出异常的add,remove,和element的阻塞队列的例子

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class BlockingQueueDemo {

    public static void main(String[] args) throws Exception {

        BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
        System.err.println(blockingQueue.add("a"));
        System.err.println(blockingQueue.add("b"));
        System.err.println(blockingQueue.add("c"));
        //        System.err.println(blockingQueue.add("d"));

        System.err.println(blockingQueue.element());

        System.err.println(blockingQueue.remove());
        System.err.println(blockingQueue.remove());
        System.err.println(blockingQueue.remove());
        //        System.err.println(blockingQueue.remove());

    }

}

当add方法的注释放开的时候,会抛出下边的错误。

true
true
true
Exception in thread "main" java.lang.IllegalStateException: Queue full
    at java.base/java.util.AbstractQueue.add(AbstractQueue.java:98)
    at java.base/java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:326)
    at BlockingQueueDemo.main(BlockingQueueDemo.java:12)
 

当remove方法的注释放开的时候,会抛出下边的错误,说明add方法,remove方法一言不和就抛出异常。

true
true
true
a
a
b
c
Exception in thread "main" java.util.NoSuchElementException
    at java.base/java.util.AbstractQueue.remove(AbstractQueue.java:117)
    at BlockingQueueDemo.main(BlockingQueueDemo.java:19)
 

    再来看,不抛出异常的offer,peek,和poll的阻塞队列的例子

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class BlockingQueueDemo {

    public static void main(String[] args) throws Exception {

        BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
        System.err.println(blockingQueue.offer("a"));
        System.err.println(blockingQueue.offer("b"));
        System.err.println(blockingQueue.offer("c"));
        System.err.println(blockingQueue.offer("d"));

        System.err.println(blockingQueue.peek());

        System.err.println(blockingQueue.poll());
        System.err.println(blockingQueue.poll());
        System.err.println(blockingQueue.poll());
        System.err.println(blockingQueue.poll());

    }

}

    从下边的执行结果来看,当往阻塞队列里追加元素的时候会返回false,以及当从空的阻塞队列中删除元素的时候,会返回null,这样程序相比直接抛出异常会显得友好些。

true
true
true
false
a
a
b
c
null

    我们再来说说上表中第三种情况的put,take方法,阻塞,即不抛异常,也不返回结果,我们在看来看一个demo

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class BlockingQueueDemo {

    public static void main(String[] args) throws Exception {

        BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        System.err.println("===============");
        //blockingQueue.put("d");

        blockingQueue.take();
        blockingQueue.take();
        blockingQueue.take();
        //blockingQueue.take();
    }

}

    首先创建容量为3的阻塞队列,当3个元素加满的时候,在想往容器中追加元素的时候,阻塞队列会一直等待,take方法也是一样的,当阻塞队列为空的时候,在想从队列当中take元素时,也是会阻塞。

    最后,我们看看第四种情况,带时间参数的阻塞队列。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class BlockingQueueDemo {

    public static void main(String[] args) throws Exception {

        BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
        System.err.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
        System.err.println(blockingQueue.offer("b", 2, TimeUnit.SECONDS));
        System.err.println(blockingQueue.offer("c", 2, TimeUnit.SECONDS));
        System.err.println(blockingQueue.offer("d", 2, TimeUnit.SECONDS));

    }

}

    从执行结果来看,设定阻塞时间后,在指定的时间内没有添加成功的话,会返回结果,不阻塞,相比阻塞的方法也得友好。

true
true
true
false

    阻塞队列以及阻塞队列的四种方法类型介绍到这里,实际的开发过程中,根据自己的业务场景来选择具体的方法类型,好的,下篇见。

你可能感兴趣的:(java,java高级,java面试,阻塞队列,阻塞队列四种方法类型,JUC包下阻塞类)