美文网首页
ThreadPoolExecutor使用注意事项

ThreadPoolExecutor使用注意事项

作者: 黑铁大魔王 | 来源:发表于2019-10-12 22:33 被阅读0次

    ThreadPoolExecutor在Java多线程编程中非常重要,JDK提供的线程池,最终都会通过new ThreadPoolExecutor(...)来创建线程池
    ThreadPoolExecutor如下

    public ThreadPoolExecutor(int corePoolSize, // 1
                                  int maximumPoolSize, // 2
                                  long keepAliveTime, // 3
                                  TimeUnit unit, // 4
                                  BlockingQueue<Runnable> workQueue, // 5
                                  RejectedExecutionHandler handler // 6
                              ) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                 Executors.defaultThreadFactory(), handler);
        }
    

    参数1: 核心线程数
    参数2: 最大线程数(容易误解,下面详细说明)
    参数3: 空闲线程存活时间
    参数4: 对于参数3的时间单位
    参数5: 保存线程的队列
    参数6: 当被提交的线程被拒绝执行时调用的方法

    好了,参数的意思大致说了下,不过可能有些大白话。下面来举例说明下

    ThreadPoolExecutor executor = new ThreadPoolExecutor(
                    1, // 核心线程数
                    2, // 最大线程数
                    60, // 存活时间
                    TimeUnit.SECONDS, // 单位秒
                    new ArrayBlockingQueue<>(4), // 工作队列,容量是4
                    new B_MyRejected() // 拒绝策略
            );
    
            B_Task t1 = new B_Task(1, "任务1");
            B_Task t2 = new B_Task(2, "任务2");
            B_Task t3 = new B_Task(3, "任务3");
            B_Task t4 = new B_Task(4, "任务4");
            B_Task t5 = new B_Task(5, "任务5");
            B_Task t6 = new B_Task(6, "任务6");
            B_Task t7 = new B_Task(7, "任务7");
    
            executor.execute(t1);
            executor.execute(t2);
    
            executor.execute(t3);
            executor.execute(t4);
    
            executor.execute(t5);
            executor.execute(t6);
            executor.execute(t7);
    

    B_Task耗时1秒,代码后面再给出
    上面的代码,首先创建了一个线程池executor
    然后,将任务t1到t7都扔进线程池执行
    那么,
    一:在executor.execute(t1);之后,会创建一个线程出来并且执行t1。由于核心线程数量=1,那么改线程不会被销毁,而是一直缓存在线程池中。
    二:由于t1需要1秒才能执行完成,并且核心线程数量=1,那么当t1执行完成之前,剩下的任务有4个就会进入工作队列等待执行(因为工作队列容量是4),这样t2,t3,t4,t5进入工作队列等待t1执行完毕
    三:剩下t6,t7,这时,工作队列已被占满,无法再缓存任务,但是,最大线程数=2,而t1已经占用了一个线程数,还剩下一个线程数可以使用,那么这时,会再次new一个新的线程执行t6(这里有种咸鱼翻身的感觉,明明后加入,却比2,3,4,5先执行)
    四:好吧,t6咸鱼翻身了,t7呢?t7就没有那么好运了,会被拒绝执行,不过被拒绝的同时也会进入拒绝策略B_MyRejected(),执行rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法中的自定义代码
    五:当所有任务运行结束后,本来存在的2个线程(最大线程数=2),会回收一个,值留下1个缓存(因为核心线程数=1)
    最终:写完了
    剩余代码片段如下:

    package thread_demo_02;
    
    /**
     * @Author: yesiming
     * @Platform: Mac
     * @Date: 5:46 PM 2019/10/12
     */
    public class B_Task implements Runnable {
    
        private int id;
        private String name;
    
        public B_Task(int id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public void run() {
            System.out.println(getId() + " ->线程执行...开始...");
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(getId() + " ->线程执行...结束...");
        }
    }
    
    package thread_demo_02;
    
    import java.util.concurrent.RejectedExecutionHandler;
    import java.util.concurrent.ThreadPoolExecutor;
    
    /**
     * @Author: yesiming
     * @Platform: Mac
     * @Date: 6:42 PM 2019/10/12
     */
    public class B_MyRejected implements RejectedExecutionHandler {
    
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            System.out.println("自定义拒绝策略...");
            System.out.println("当前被拒绝的任务是:" + ((B_Task)r).getId());
    
            // TODO: 下面就可以
            // 1. 记录日志,在非高峰期,再根据日志从新处理(推荐)
            // 2. 放到缓存里,下次在执行(不推荐,Queue满了才会拒绝执行,此时应该考虑到了内存容量问题,那么也就不要往内存里放数据了)
            // 4. 其他做法,我就不再叨叨了
        }
    }
    

    相关文章

      网友评论

          本文标题:ThreadPoolExecutor使用注意事项

          本文链接:https://www.haomeiwen.com/subject/mvvcmctx.html