java并发编程之 线程池的使用

最近看了java编程艺术之后,又看了一边线程池源码,发现自己很多瞎几把用的地方 mark一下
对于线程池的参数理解,和线程池大小的设置

线程池的参数理解

平常工作通常就是

复制

new  ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue
                         )

黏贴


new ThreadPoolExecutor(
            0,
            8,
            30,
            TimeUnit.MINUTES,
            new LinkedBlockingQueue<Runnable>()
            }

0代表核心线程,8代表最大线程数量,30代表超时时间,TimeUnit.MINUTES表示时间单位,new LinkedBlockingQueue() 代表队列
一直这么用也没出问题。

一直到今天发现,没出问题纯粹是运气好

把上面的代码稍稍做出改变

new ThreadPoolExecutor(
            0,
            8,
            30,
            TimeUnit.MINUTES,
            new LinkedBlockingQueue<Runnable>(10)
            }

没错,就是在workQueue当中加一个10,RejectedExecutionException 分分钟出现

为什么那?????

第一个侥幸 在创建工作队列时,使用了无参方法,默认使用了int的最大值创建工作队列,所以这个队列的最大容量可以达到2147483648 21亿基本上的公司都达不到这个量 所以侥幸逃过

那么为什么 队列容量缩小之后就立马出现问题了呐?
这个时候我们就要看到线程池的创建代码

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

在5个参数的构造方法下面,有调用了7参的方法,加入了默认的线程工厂和默认的拦截策略AbortPolicy,

  private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();

java.util.concurrent.ThreadPoolExecutor 类中对拦截策略定义了四种模式

  1. CallerRunsPolicy :这个策略重试添加当前的任务,他会自动重复调用 execute() 方法,直到成功。
  2. AbortPolicy :对拒绝任务抛弃处理,并且抛出异常。
  3. DiscardPolicy :对拒绝任务直接无声抛弃,没有异常信息。
  4. DiscardOldestPolicy :对拒绝任务不抛弃,而是抛弃队列里面等待最久的一个线程,然后把拒绝任务加到队列。

所以当提交任务数超过maxmumPoolSize+workQueue之和时 也就是示例当中 8+10 超过19个任务的时候,
直接抛出异常 RejectedExecutionException

    public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

mark 扩展

仔细观察这个类的实现,继承 RejectedExecutionHandler 重写 rejectedExecution,那么是否可以自定义 拦截策略呐?
写一个CustomRejectedExecutionHandler 类 继承 RejectedExecutionHandler 重写 rejectedExecution

   private class CustomRejectedExecutionHandler implements RejectedExecutionHandler {

       @Override
       public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
           try {
            do something
            System.out.println("is come");
           } catch (InterruptedException e) {
               e.printStackTrace();
               System.out.println("error.............");
           }
       }
   }
   
new ThreadPoolExecutor(
               10,
               30,
               30,
               TimeUnit.MINUTES,
               new LinkedBlockingQueue<Runnable>(10),
               Executors.defaultThreadFactory(),
              new CustomRejectedExecutionHandler() );

此时,当我们队列满的时候,就会进入自己的拒绝策略
重写工厂方法也是同理,继承ThreadFactory 重写 newThread方法

线程池的大小设置

通常的百度,都会告诉我们线程池的大小设置分为 io密集型和 cpu密集型,cpu密集型则,线程池大小为 cpu数量, io密集型则为 cpu*2+1
《java开发变成实战》 中给出这样一个公式

 Ncpu = CPU的数量
        
  Ucpu = 目标CPU的使用率, 0 <= Ucpu <= 1

  W/C = 等待时间与计算时间的比率

  为保持处理器达到期望的使用率,最优的池的大小等于:

  Nthreads = Ncpu x Ucpu x (1 + W/C)

windos环境下 4核心8线程的电脑

写了一段纯cpu代码

                long sub=0;
                for(long i=0;i<count;i++){
                    sub+=i;
                }

count的值等于 1000000000L 时,单线程计算大约为500毫秒
count的值等于 10000000000L 时,单线程计算大约为2628毫秒

在这里插入图片描述
图上只是一小部分数据,实际测试更多次,得出以下结果

实测发现如下特点
1, 8个线程时,单个任务时间基本保持单线程差不多,约等于500毫秒,
100线程时,单个任务的时间上升到2000毫秒左右
说明频繁的上下文切换存在。线程并不是越多越好
2,500ms任务执行数量上升时,100线程反而最快,当任务数量上涨时,优势被正比扩大
,当线程时间大时,存在 线程数量=cpu数量的最优结果

得出结论,公式只是一个参考值,切换和调度并不受控制,实际情况需要根据压测结果设置。特别是当线程的cpu时间较短时。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
校园失物招领系统管理系统按照操作主体分为管理员和用户。管理员的功能包括字典管理、论坛管理、公告信息管理、失物招领管理、失物认领管理、寻物启示管理、寻物认领管理、用户管理、管理员管理。用户的功能等。该系统采用了Mysql数据库,Java语言,Spring Boot框架等技术进行编程实现。 校园失物招领系统管理系统可以提高校园失物招领系统信息管理问题的解决效率,优化校园失物招领系统信息处理流程,保证校园失物招领系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 ,管理员权限操作的功能包括管理公告,管理校园失物招领系统信息,包括失物招领管理,培训管理,寻物启事管理,薪资管理等,可以管理公告。 失物招领管理界面,管理员在失物招领管理界面中可以对界面中显示,可以对失物招领信息的失物招领状态进行查看,可以添加新的失物招领信息等。寻物启事管理界面,管理员在寻物启事管理界面中查看寻物启事种类信息,寻物启事描述信息,新增寻物启事信息等。公告管理界面,管理员在公告管理界面中新增公告,可以删除公告。公告类型管理界面,管理员在公告类型管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值