自定义线程池的重要参数------阻塞队列

目录

概述

ArrayBlockingQueue

LinkedBlockingQueue

注意事项

DelayedWorkQueue

PriorityBlockingQueue

注意

SynchronousQueue

总结


概述

BlockingQueue阻塞队列接口,是一个支持两个附加操作的队列。

这两个附加操作分别是:

  • 在队列为空时,获取元素的线程会等待队列变为非空。
  • 当队列元素已满时,存储元素的线程会等待队列可用。

阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是往队列里拿出元素的线程。阻塞队列的角色就是充当生产者存放元素的容器,而消费者只从容器里拿出元素,这也在一定程度上平衡了二者之间的处理能力,解决了它们之间的强耦合关系,在保证并发安全的同时,提高了队列的存取效率。

ArrayBlockingQueue

ArrayBlockingQueue,顾名思义,它是基于数组实现的,所以它是一个有界队列,在它的内部维护了一个定长数组,以便缓存队列中的数据对象,按照FIFO的原则排序;

入队与出队的操作,使用同一个ReentrantLock来进行控制。

下面是它的部分源码:

public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
    
    /** The queued items */

    // ArrayBlockingQueue使用定长数组做为存储结构
    final Object[] items;

    /** Main lock guarding all access */
    final ReentrantLock lock;
    
    // 创建时传入数组容量(长度)
    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);

    }

}

我们通过它来自定义一个线程池:

ExecutorService executorService =
                new ThreadPoolExecutor(4,
                        10,
                        2, TimeUnit.SECONDS,
                        new ArrayBlockingQueue<Runnable>(10),
                        Executors.defaultThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy());

使用ArrayBlockingQueue的意义在于: 通过使用有界队列,让maximumPoolSize最大线程数参数有意义。

原因:

若有新的任务需要执行,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,才会将新的任务加入到工作队列中。若队列已满,也就是超过数组的长度时,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。

在这种情况下,线程池的线程数量上限与有界队列的容量有直接关系。如果有界队列初始容量较大或者没有达到超负荷的状态,线程数将一直维持在corePoolSize以下,反之当任务队列已满时,才会创建核心线程之外的非核心线程,这样maximumPoolSize这个参数才有了意义。

LinkedBlockingQueue

LinkedBlockingQueue是一个无界队列,基于单向链表结构,可以选择进行设置容量。如果不设置容量的化话,最大长度为Integer.MAX_VALUE。

入队和出队操作使用不同的ReentrantLock来进行控制,所以LinkedBlockingQueue的吞吐量通常高于ArrayBlockingQueue。

下面是它的部分源代码:

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    // 单向链表Node节点
    static class Node<E> {
        E item;

        /**
         * One of:
         * - the real successor Node
         * - this Node, meaning the successor is head.next
         * - null, meaning there is no successor (this is the last node)
         */
        Node<E> next;

        Node(E x) { item = x; }
    }
    
    // 存取操作使用两把不同的锁来进行管理    

    /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Lock held by put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    // 默认按照Integer.MAX_VALUE设置容量
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }
}

 FixedThreadPool、SingleThreadExecutor线程池都使用LinkedBlockingQueue 队列;

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());
    }

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                        0L, TimeUnit.MILLISECONDS,
                        new LinkedBlockingQueue<Runnable>()));
    }

注意事项

  • 如果使用无界队列,根据线程池的执行流程来看,一旦corePoolSize达到最大时,那么将会无限制地添加线程任务到队列中,那么也就意味着maximumPoolSize这个参数是无效的;
  • 所以当我们使用这种任务队列模式时,一定要注意任务提交与任务处理之间的协调与控制,在高并发的环境下,会导致大量的线程任务堆积到阻塞队列中而得不到及时的处理。

DelayedWorkQueue

DelayedWorkQueue是基于堆结构的延迟队列,基于数组实现,初始容量为16,leader线程用于获取堆顶元素(队列头部元素)。该队列根据指定的延迟时间从小到大排序,如果延迟时间相同,则根据插入到队列的先后顺序排序。

下面是它的部分源码:

static class DelayedWorkQueue extends AbstractQueue<Runnable>
        implements BlockingQueue<Runnable> {
    private static final int INITIAL_CAPACITY = 16;
    private RunnableScheduledFuture<?>[] queue = new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
    private Thread leader = null;
}

 ScheduledThreadPool线程池使用了这个队列。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

    //.....

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                new DelayedWorkQueue());
    }

PriorityBlockingQueue

PriorityBlockingQueue是一个基于优先级的无界队列(优先级的判断通过构造方法传入Comparator比较器或者通过元素实现Comparable接口来决定)。

注意事项

  1. PriorityBlockingQueue并不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者。因此使用的时候要特别注意,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。
  2. PriorityBlockingQueue它其实是一个特殊的无界队列,它其中无论添加了多少个任务,线程池创建的线程数也不会超过corePoolSize的数量,只不过其他队列一般是按照先进先出的规则处理任务,而PriorityBlockingQueue队列可以自定义规则根据任务的优先级顺序先后执行。

SynchronousQueue

SynchronousQueue是一个同步队列,它是一个不存储元素的阻塞队列(内部没有保存元素的数据结构容器),每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue。

CachedThreadPool线程池使用了这个队列。

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>());
    }

下面是根据SynchronousQueue 创建的线程池:

public class Main {
    public static void main(String[] args) {

        // maximumPoolSize设置为2 ,拒绝策略为AbortPolic策略(直接抛出异常)
        ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 10, TimeUnit.MILLISECONDS,
                new SynchronousQueue<Runnable>(),
                new ThreadPoolExecutor.AbortPolicy());

        // 执行的线程任务大于maximumPoolSize,执行拒绝策略
        for (int i = 1; i <= 3; i++) {
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "被执行!");
                }
            });
        }

        // 关闭线程池
        pool.shutdown();
    }
}

当使用SynchronousQueue创建线程池时, 创建的线程数大于maximumPoolSize时,直接执行了拒绝策略抛出异常。

使用SynchronousQueue队列,提交的任务不会被保存,总是会马上提交执行。如果用于执行任务的线程数量小于maximumPoolSize,则尝试创建新的线程,如果达到maximumPoolSize设置的最大值,则会根据设置的handler参数执行拒绝策略。

所以CachedThreadPool这个线程池的最大线程数为Integer.MAX_VALUE,就是为了防止线程任务的数量大于最大线程数,线程池执行拒绝策略。

那么在这种情况下,开发者需要对程序的并发量有个准确的预估,才能设置合适的maximumPoolSize数量,否则很容易就会导致线程池执行拒绝策略。

总结

将这五个线程池可以分为:

  • 有界队列: ArrayBlockingQueue,DelayedWorkQueue
  • 无界队列: LinkedBlockingQueue,PriorityBlockingQueue
  • 同步队列: SynchronousQueue

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值