线程池的三种缓存队列

最近在看java的线程池,对于里面的三种缓存队列里面进行对比学习了下,感觉自己测试下来的结果和网上有些知识点不同相同,所以还希望有人能帮我解惑下。

概述

队列简单解释
SynchrousQueue不会保存提交任务,超出直接corePoolSize个任务,直接创建新的线程来执行任务,直到(corePoolSize+新建线程)> maximumPoolSize。不是核心线程就是新建线程。
LinkedBlockingQueue基于链表的先进先出,无界队列。超出直接corePoolSize个任务,则加入到该队列中,直到资源耗尽,所以maximumPoolSize不起作用
ArrayBlockingQueue基于数组的先进先出,创建时必须指定大小,超出直接corePoolSize个任务,则加入到该队列中,只能加该queue设置的大小,其余的任务则创建线程,直到(corePoolSize+新建线程)> maximumPoolSize

SynchrousQueue

无缓冲等待队列,是一个不存储的元素阻塞队列。

创建一个corePoolSize为2,maximumPoolSize为3的线程池。执行6个任务。根据我的理解,SynchrousQueue是个一个无缓存的队列(理论依据为SynchrousQueue源码可以看到:isEmpty()始终为true;size()始终返回0)

根据参数设置应该只可以执行3个任务:

2个核心线程执行2个任务;
第3个任务的时候,创建线程来执行任务3;
当第4个任务来的时候,此时已经超过了maximumPoolSize,所以拒绝任务。(但是网上有些说法是这里是可以在缓存一个任务到队列中,我不太理解这种说法,还希望有人能解答下。)

创建线程池代码如下:


    /**
     * SynchronousQueue
     */
    private static void syncQueue() {
        System.out.println("\n\n =======SynchronousQueue====== \n\n");
        Executor executors = new ThreadPoolExecutor(
                2, 3, 30, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(),
                new RejectHandler());
        execute(executors);
    }

执行结果如下:(跟我的预想是一样的)

1 is running... 
4 is rejected ^^ //4被拒
2 is running... 
3 is running... 
5 is rejected ^^  //5被拒
6 is rejected ^^  //6被拒
3 is end !!! 
1 is end !!! 
2 is end !!! 

 ArrayBlockingQueue

有界缓存队列,可以指定缓存数量。有任务的时候,会先放入缓存队列中,当超出缓存数量的时候,在创建没有超出maximumPoolSize的线程数量,直到超过maximumPoolSize之后报错

创建一个corePoolSize为2,maximumPoolSize为3的线程池。其中ArrayBlockingQueue设置缓存2个任务。执行6个任务。ArrayBlockingQueue为有界队列:

任务1和2在核心线程中执行;
任务3和4进来的时候,是放到ArrayBlockingQueue缓存队列中的,并且只能放2个(ArrayBlockingQueue设置的大小为2);
任务5和6进来的时候,任务5新建线程来执行任务,已经达到最大线程数3,所以任务6拒绝;
当有线程执行完的时候,再将任务3和4从队列中取出执行

创建线程池代码如下:

 

  /**
     * ArrayBlockingQueue
     */
    private static void arrayQueue() {
        System.out.println("\n\n =======ArrayBlockingQueue====== \n\n");
        Executor executors = new ThreadPoolExecutor(
                2, 3, 30, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(2),
                new RejectHandler());
        execute(executors);
    }

 

执行结果如下

1 is running... 
2 is running... 
6 is rejected ^^ //6被拒
5 is running...  //5新建线程执行
1 is end !!! 
2 is end !!! 
3 is running... //1和2执行完之后3和4才执行
4 is running... 
5 is end !!! 

LinkedBlockingQueue

无界缓冲队列,剩余的元素会在阻塞队列中等待。只会创建corePoolSize个线程

创建一个corePoolSize为2,maximumPoolSize为3的线程池。无界队列。同样执行6个任务

核心线程执行任务1和2,其它的任务3~6放到队列中
执行完1和2,将3和4从队列中取出执行
执行完3和4,将5和6从队列中取出

    /**
     * LinkedBlockingQueue
     */
    private static void linkedQueue() {
        System.out.println("\n\n =======LinkedBlockingQueue====== \n\n");
        Executor executors = new ThreadPoolExecutor(
                2, 3, 30, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(),
                new RejectHandler());
        execute(executors);
    }

运行结果如下:

1 is running... 
2 is running... //中间线程休眠 
2 is end !!!   //10s之后才运行完
1 is end !!! 
3 is running...  //任务3和4才执行
4 is running... 
4 is end !!! 
3 is end !!! 
6 is running... 
5 is running... 
5 is end !!! 
6 is end !!! 

总结

队列创建线程数正在可以去有执行意愿(放入到缓存队列和执行线程中)的最多任务数可以缓存个数
SynchrousQueuemaximumPoolSize(corePoolSize+创建的线程数) <=maximumPoolSize
ArrayBlockingQueuemaximumPoolSize(corePoolSize+创建的线程数)(相加数要<=maximumPoolSize)+ArrayBlockingQueue设置的缓存数ArrayBlockingQueue设置的缓存数
LinkedBlockingQueuecorePoolSizecorePoolSize+LinkedBlockingQueue缓存的无界任务无界

总的代码如下:

 public static void main(String[] args) {
        queue();  
    }

    /***
     * 执行不同的缓存队列
     */
    private static void queue() {
        syncQueue();
        //arrayQueue();
        //linkedQueue();
    }

    /**
     * ArrayBlockingQueue
     */
    private static void arrayQueue() {
        System.out.println("\n\n =======ArrayBlockingQueue====== \n\n");
        Executor executors = new ThreadPoolExecutor(
                2, 3, 30, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(2),
                new RejectHandler());
        execute(executors);
    }

    /**
     * LinkedBlockingQueue
     */
    private static void linkedQueue() {
        System.out.println("\n\n =======LinkedBlockingQueue====== \n\n");
        Executor executors = new ThreadPoolExecutor(
                2, 3, 30, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(),
                new RejectHandler());
        execute(executors);
    }

    /**
     * SynchronousQueue
     */
    private static void syncQueue() {
        System.out.println("\n\n =======SynchronousQueue====== \n\n");
        Executor executors = new ThreadPoolExecutor(
                2, 3, 30, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(),
                new RejectHandler());
        execute(executors);
    }


    private static void execute(Executor executors) {
        executors.execute(new NameRunnable(1));
        executors.execute(new NameRunnable(2));
        executors.execute(new NameRunnable(3));
        executors.execute(new NameRunnable(4));
        executors.execute(new NameRunnable(5));
        executors.execute(new NameRunnable(6));
    }

 /***
     * 创建一个有名字的runnable对象
     */
    private static class NameRunnable implements Runnable {
        private int name;

        public NameRunnable(int name) {
            this.name = name;
        }

        public int getName() {
            return name;
        }

        @Override
        public void run() {
            System.out.println(name + " is running... ");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + " is end !!! ");
        }
    }

    /***
     * 拒绝的Runnable
     */
    private static class RejectHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            NameRunnable name = (NameRunnable) r;

            System.out.print(name.getName() + " is rejected ^^\n");
        }
    }
}

 

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Java中,线程池和阻塞队列(BlockingQueue)的选择与设置是非常关键的,因为它们直接影响了系统的并发性能和资源管理。线程池中的阻塞队列用于存储任务,当线程池中的工作线程空闲时,会从队列中取出任务执行;反之,如果任务队列满了,新提交的任务会被阻塞,直到队列中有空间。 决定队列大小的因素通常包括: 1. **系统负载**:考虑应用程序的平均请求速率以及峰值负载。如果预计请求量波动较大,可能需要较大的缓冲能力来应对高峰期。 2. **硬件限制**:比如内存大小。队列过大会消耗过多内存,影响其他部分的性能。队列过小可能导致频繁地创建和销毁线程,增加上下文切换成本。 3. **任务类型**:对于I/O密集型任务,队列可以适当大一些,因为它不需要立即处理结果;而对于计算密集型任务,队列可能需要更小,以免CPU等待IO完成。 4. **超时策略**:如果你希望在队列满时设定一个时间限制(如生产者阻塞的时间),那么队列大小应该足够容纳这个超时时间内的任务。 常用的Java阻塞队列有`ArrayBlockingQueue`、`LinkedBlockingQueue`、`PriorityBlockingQueue`等,它们的配置参数`容量`就是指队列的最大元素数量。一般来说,初始容量可以设置为核心线程数的1.5到3倍,然后根据实际情况调整。同时,也可以设置无界队列(如`LinkedList`或自定义无限大小的队列),但这样可能会导致大量内存占用。 设置合适的队列大小需要进行实际监控和测试,找到一个既满足吞吐需求又能保证系统稳定性的平衡点。如果你提供具体的应用场景或数据,我可以给出更具体的建议。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值