8、ThreadPoolExecutor源码

本文详细介绍了Java线程池的几种类型,包括可缓存线程池、指定工作线程的线程池和定时线程池等,并深入解析了ThreadPoolExecutor的工作原理、执行流程及关键属性。

一、线程池有哪些类

1、可缓存线程池(NewCachedThreadPool)

创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行
很多短期异步任务的程序而言,这些线程池通常可提高程序性能。


2、 指定工作线程的线程池(NewFixedThreadPool)

创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。


3、定时的线程池(newScheduledThreadPool)

创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

4、ThreadPoolExecutor

ThreadPoolExecutor与前几个线程池对比要更灵活

二、ThreadPoolExecutor解决什么问题

创建一个新的线程可以通过继承Thread类或者实现Runnable接口来实现,这两种方式创建的线程在运行结束后会被虚拟机销毁,进行垃圾回收,如果线程数量过多,频繁的创建和销毁线程会浪费资源,降低效率。而线程池的引入就很好解决了上述问题,线程池可以更好的创建、维护、管理线程的生命周期,做到复用,提高资源的使用效率,也避免了开发人员滥用new关键字创建线程的不规范行为。


1、ThreadPoolExecutor数据结构

ThreadPoolExecutor数据结构有 HashSetBlockingQueue、Worker、AbstractQueuedSynchronizer构成。

2、ThreadPoolExecutor执行流程

1、任务添加 - 当线程池任务数小于核心线程数时,Worker 是继承Runnable的包装类,每一个任务都使用Worker包装起来,然后将包装对象存入HashSet中,直接唤醒其线程执行。 

2、任务添加 - 当线程池任务数大于核心线程数时,执行方法直接放入BlockingQueue队列中,排队执行。如果队列满了执行队列拒绝策略。

3、线程回收- 当执行线程大于核心线程 或者 线程池关闭 或者 核心线程keepAliveTimeOut超时时候触发interruptIdleWorkers方法。 注意点interruptIdleWorkers回收原则是线程是否被发出中断请求,如果被发出中断请求执行回收。

三、ThreadPoolExecutor类重点方法列举

属性名称解释功能描述
keepAliveTime线程空闲时间一个线程处在空闲状态的时间超过了该属性值,就会因为超时而退出。举个例子,如果线程池的核心大小corePoolSize=5,而当前大小poolSize =8,那么超出核心大小的线程,会按照keepAliveTime的值判断是否会超时退出。如果线程池的核心大小corePoolSize=5,而当前大小poolSize =5,那么线程池中所有线程都是核心线程,这个时候线程是否会退出,取决于allowCoreThreadTimeOut
allowCoreThreadTimeOut核心线程超时关闭该属性用来控制是否允许核心线程超时退出。默认值为:false。如果线程池的大小已经达到了corePoolSize,不管有没有任务需要执行,线程池都会保证这些核心线程处于存活状态。该属性只是用来控制核心线程的
poolSize线程池中当前线程的数量线程池中当前线程的数量,当该值为0的时候,意味着没有任何线程
corePoolSize核心线程数线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。这里需要注意的是:在刚刚创建ThreadPoolExecutor的时候,线程并不会立即启动,而是要等到有任务提交时才会启动,除非调用了prestartCoreThread/prestartAllCoreThreads事先启动核心线程。再考虑到keepAliveTime和allowCoreThreadTimeOut超时参数的影响,所以没有任务需要执行的时候,线程池的大小不一定是corePoolSize
maximumPoolSize线程池中允许的最大线程数线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。这里值得一提的是largestPoolSize,该变量记录了线程池在整个生命周期中曾经出现的最大线程个数。为什么说是曾经呢?因为线程池创建之后,可以调用setMaximumPoolSize()改变运行的最大线程的数目
queueCapacity任务队列容量(阻塞队列当核心线程数达到最大时,新任务会放在队列中排队等待执行
rejectedExecutionHandler任务拒绝处理器 * 两种情况会拒绝处理任务:
       - 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
       - 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
        * 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常
        * ThreadPoolExecutor类有几个内部实现类来处理这类情况:
            - AbortPolicy 丢弃任务,抛运行时异常
            - CallerRunsPolicy 执行任务
            - DiscardPolicy 忽视,什么都不会发生
            - DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
        * 实现RejectedExecutionHandler接口,可自定义处理器

四、线程池源码解读

1、任务添加

1、当前线程数小于核心线程数,addWoker直接添加入Hashset中,直接唤醒addWorker线程执行。

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();

        //线程小于核心线程数,addWorker添加Hashset直接唤醒线程执行
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        
        //任务添加到队列
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }

        //任务添加队列失败执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
}

2、队列任务执行

线程池队列线程执行情况

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // 如果线程池停止执行线程回收
                // 如果当前线程被中断
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
  }

3、线程池线程回收

线程回收方法是线程安全的,如果线程被中断执行回收。

//线程池线程回收
private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值