ThreadPoolExecutor线程池工作流程详解

一.ThreadPoolExecutor的参数介绍

// corePoolSize:核心线程数
// maximumPoolSize:最大线程数
// keepAliveTime:空闲线程存活时长
// unit:时间单位
// workQueue:任务队列
// threadFactory:线程工厂
// handler:拒绝策略
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {

常用的阻塞队列BlockingQueue

1. ArrayBlockingQueue

  • ArrayBlockingQueue 是一个用数组实现的有界阻塞队列;
  • 队列满时插入操作被阻塞,队列空时,移除操作被阻塞;
  • 按照先进先出(FIFO)原则对元素进行排序;
  • 默认不保证线程公平的访问队列;
  • 公平访问队列:按照阻塞的先后顺序访问队列,公平性会降低吞吐量。

2. LinkedBlockingQueue

  • 一个基于链表结构的阻塞队列;
  • 此队列按 FIFO 排序元素,吞吐量通常要高于ArrayBlockingQueue;
  • 静态工厂方法 Executors.newFixedThreadPool()使用了这个队列;
  • LinkedBlockingQueue具有单链表和有界阻塞队列的功能;
  • 队列满时插入操作被阻塞,队列空时,移除操作被阻塞;
  • 默认和最大长度为Integer.MAX_VALUE,相当于无界 (值非常大:2^31-1)。

3. SynchronousQueue

  • 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue;
  • 静态工厂方法 Executors.newCachedThreadPool()使用这个队列。
  • SynchronousQueue本身不存储数据,调用了put方法后,队列里面也是空的;
  • 每一个put操作必须等待一个take操作完成,否则不能添加元素。

4. PriorityBlockQueue

  • 一个具有优先级的无限阻塞队列。

已实现的拒绝策略

  • AbortPolicy:默认策略;新任务提交时直接抛出异常RejectedExecutionException。
  • CallerRunsPolicy:使用调用者所在线程运行新的任务。
  • DiscardPolicy:丢弃新的任务,且不抛出异常。
  • DiscardOldestPolicy:调用poll方法丢弃工作队列队头的任务,然后尝试提交新任务

如果上面的拒绝策略不能满足你的需求,也可以自定义拒绝策略,只需要实现RejectedExecutionHandler 接口即可。

二.线程池的工作流程

下面的流程图即为整个线程池的工作流程

 代码证明

自定义的线程工厂

static class MyTreadFactory implements ThreadFactory {

        private final AtomicInteger threadNum = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r, "my-thread-" + threadNum.getAndIncrement());
            System.out.println(thread.getName() + " has been created");
            return thread;
        }
    }

自定义的拒绝策略 

static class MyRejectedPolicy implements RejectedExecutionHandler {
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            MyThread myThread = (MyThread)r;
            int num = myThread.getNum();
            System.err.println( "任务"+ num + "拒绝");
        }
    }

自定义的线程

static class MyThread implements Runnable {
        private final int num;

        public MyThread(int num) {
            this.num = num;
        }

        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + "--开始执行" + num + "任务");
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "--执行结束"+ num + "任务");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public int getNum() {
            return num;
        }
    }

测试场景一:任务数 <= 核心线程数 + 队列容量

public static void main(String[] args) {
        // 核心线程为 3 个
        // 最大线程数为 7 个
        // 队列容量为  7 个
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                3, 7, 1, TimeUnit.MINUTES,
                new ArrayBlockingQueue<>(7), new MyTreadFactory(), new MyRejectedPolicy());

        // 提交 10 个任务
        for (int i = 0; i < 10; i++) {
            MyThread myThread = new MyThread(i);
            threadPoolExecutor.execute(myThread);
        }

    }

执行结果:

my-thread-1 has been created
my-thread-2 has been created
my-thread-3 has been created
my-thread-2--开始执行1任务
my-thread-1--开始执行0任务
my-thread-3--开始执行2任务
my-thread-3--执行结束2任务
my-thread-3--开始执行3任务
my-thread-1--执行结束0任务
my-thread-1--开始执行4任务
my-thread-2--执行结束1任务
my-thread-2--开始执行5任务
my-thread-1--执行结束4任务
my-thread-2--执行结束5任务
my-thread-1--开始执行6任务
my-thread-2--开始执行7任务
my-thread-3--执行结束3任务
my-thread-3--开始执行8任务
my-thread-1--执行结束6任务
my-thread-3--执行结束8任务
my-thread-1--开始执行9任务
my-thread-2--执行结束7任务
my-thread-1--执行结束9任务

 结论:从上述的执行结果得出,一共 10 个任务,启动了 3 个核心线程,7 个任务是先放在队列中等待空闲线程去执行。


测试场景二:任务数 > 核心线程数 + 队列容量

public static void main(String[] args) {
        // 核心线程为 3 个
        // 最大线程数为 7 个
        // 队列容量为  5 个
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                3, 7, 1, TimeUnit.MINUTES,
                new ArrayBlockingQueue<>(5), new MyTreadFactory(), new MyRejectedPolicy());

        // 提交 10 个任务
        for (int i = 0; i < 10; i++) {
            MyThread myThread = new MyThread(i);
            threadPoolExecutor.execute(myThread);
        }

    }

执行结果:

my-thread-1 has been created
my-thread-2 has been created
my-thread-3 has been created
my-thread-4 has been created
my-thread-5 has been created
my-thread-2--开始执行1任务
my-thread-1--开始执行0任务
my-thread-4--开始执行8任务
my-thread-5--开始执行9任务
my-thread-3--开始执行2任务
my-thread-3--执行结束2任务
my-thread-1--执行结束0任务
my-thread-1--开始执行3任务
my-thread-4--执行结束8任务
my-thread-4--开始执行5任务
my-thread-2--执行结束1任务
my-thread-2--开始执行6任务
my-thread-5--执行结束9任务
my-thread-3--开始执行4任务
my-thread-5--开始执行7任务
my-thread-4--执行结束5任务
my-thread-1--执行结束3任务
my-thread-2--执行结束6任务
my-thread-3--执行结束4任务
my-thread-5--执行结束7任务

结论: 从上述的执行结果得出,一共 10 个任务,启动了 5 个线程,其中 3 个为核心线程,2 个为普通线程。因为队列只能装 5 个任务, 加上核心线程执行的 3 个任务,所有剩下的 2 个任务需要启动普通线程执行。

测试场景三:任务数 > 最大线程数 + 队列容量 

public static void main(String[] args) {
        // 核心线程为 3 个
        // 最大线程数为 7 个
        // 队列容量为  5 个
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                3, 7, 1, TimeUnit.MINUTES,
                new ArrayBlockingQueue<>(5), new MyTreadFactory(), new MyRejectedPolicy());
        
        // 提交 13 个任务
        for (int i = 0; i < 13; i++) {
            MyThread myThread = new MyThread(i);
            threadPoolExecutor.execute(myThread);
        }

    }

执行结果:

my-thread-1 has been created
my-thread-2 has been created
my-thread-3 has been created
my-thread-4 has been created
my-thread-5 has been created
my-thread-6 has been created
my-thread-7 has been created
my-thread-2--开始执行1任务
my-thread-7--开始执行11任务
my-thread-3--开始执行2任务
my-thread-5--开始执行9任务
my-thread-1--开始执行0任务
my-thread-4--开始执行8任务
my-thread-6--开始执行10任务
任务12拒绝
my-thread-1--执行结束0任务
my-thread-3--执行结束2任务
my-thread-3--开始执行3任务
my-thread-6--执行结束10任务
my-thread-5--执行结束9任务
my-thread-5--开始执行6任务
my-thread-2--执行结束1任务
my-thread-2--开始执行7任务
my-thread-4--执行结束8任务
my-thread-7--执行结束11任务
my-thread-6--开始执行5任务
my-thread-1--开始执行4任务
my-thread-2--执行结束7任务
my-thread-3--执行结束3任务
my-thread-5--执行结束6任务
my-thread-6--执行结束5任务
my-thread-1--执行结束4任务

 结论:从上述的执行结果得出,一共 13个任务,启动了 7 个线程,其中 3 个为核心线程,4 个为普通线程。因为队列只能装 5 个任务, 最大线程数为 7 ,最大只能接受 12 个任务,所以最后 1 个任务被拒绝了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值