美文网首页
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