线程池源码解析


title: 线程池源码剖析
date: 2018-5-21 14:18:40
categories:
- JDK
tags:
- JDK
- 代码重构
- 源码学习

摘要:JDK1.8线程池源码剖析

多线程编程中,为每一个任务分配一个线程是不可行的。那么,如何来管理我们的线程,减少系统的性能开销就是我们研究线程池的动力。

JDK1.8

线程池的生命周期

在多线程的环境下为每个任务都分配一个线程是不现实的。线程创建的开销和资源消耗都是很高。所以我们采用线程池的方式来管理我们的线程。
合理的使用线程池能够带来3个好处。

  1. 降低资源消耗
  2. 提高响应速度
  3. 提高了现成的可管理型

线程池的实现原理

当我们向一个线程池提交了一个任务之后,线程池如何处理这个线程呢?
在讨论线程池如何管理线程,我们可以先来看一下,在ThreadPoolExecutor中,线程的生命周期。
线程池使用CAPACITY标志的高3位来表示运行状态

  1. RUNNING:接收新任务,并且处理任务队列中的任务
  2. SHUTDOWN:不接收新任务,但是处理任务队列的任务
  3. STOP:不接收新任务,不出来任务队列,同时中断所有进行中的任务
  4. TIDYING:所有任务已经被终止,工作线程数量为0,到达该状态会执行terminated()
  5. TERMINATED:terminated()执行完毕
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

那个ctl就是标志,是一个原子类

  1. 当我们向线程池提交一个线程时,如果当前运行的线程少于corePollSize,则创建新线程来执行任务
  2. 如果运行的线程数 >= corePoolSize 将任务放入BlockingQueue
  3. 如果BlockingQueue已满的话,穿件新的线程来处理任务
  4. 如果线程数超过了maximumPoolSize的话,任务将会被拒绝,并调用rejectedExecution(Runnable r, ThreadPoolExecutor executor);
    当然线程的运行要封装为Work才能在运行在线程池中,
    具体的源代码如下
 public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    int c = ctl.get(); // 获取 线程池数目
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))// 对应我上面的1
            return;
        c = ctl.get();
    }
    // 如果线程数多于了corePoolSize,或者线程创建失败,则把command放入到工作队列中去
    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);
}

线程池的核心参数:

  • corePoolSize:最小存活的工作线程数量(如果设置allowCoreThreadTimeOut,那么该值为 0)
  • maximumPoolSize:最大的线程数量,受限于CAPACITY
  • keepAliveTime:对应线程的存活时间,时间单位由TimeUnit指定
  • workQueue:工作队列,存储待执行的任务
  • RejectExecutionHandler:拒绝策略,线程池满后会触发
  • ThreadFactory 用于创建线程的工厂,
  • keepAliveTime 线程活动保持时间,当线程池的工作线程空闲之后,保持存活的时间。
  • TimeUnit:线程活动保持时间的单位,可选的有 DAYS,HOURS,MINUTES…
RejectExecutionHandler策略
  • AbortPolicy:默认策略,终止任务,抛出RejectedException
  • CallerRunsPolicy:在调用者线程执行当前任务,不抛异常
  • DiscardPolicy: 抛弃策略,直接丢弃任务,不抛异常
  • DiscardOldersPolicy:抛弃最老的任务,执行当前任务,不抛异常
如何向线程池提交任务线程呢?

可以使用execute()和submit()方法。
void execute(Runnable command);
或者使用ExecutorService的’ Future submit(Callable task);’

Executor ExecutorService接口

原谅我画图的拙劣偷作者一幅图,Executor框架的架构图。

Executor框架主要由三个部分组成。

任务

需要实现Runnable 或者 Callable接口
Executor框架主要有两个关键的实现类ThreadPoolExecutor ScheduledThreadPoolExecutor实现了ExecutorService接口

jisuanjieguo

实现了 Future接口和实现了Future接口的FutureTask类

Executor框架成员

  • ThreadPoolExecutor
    ThreadPoolExecutor使用工厂类Executors来创建,Executors可以创建三种类型的ThreadPoolExecutor
  1. FixedThreadPool
    再偷一张
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

FixedThreadPool适用于为了满足资源需要管理,限制线程数的场景,用于负载较重的服务器
2. SingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

单线程的线程池,无需多说
3. CachedThreadPool
CachedThreadPool是一个无界线程池,所谓无界就是没有界限了啦。适用短期任务小程序,负载较轻的服务器,负载较重那搞一个无界服务器会崩掉的啦。

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

这三个ThreadPoolExecutor的子类线程池主要的区别就在于构造参数workQueue上。
这个WorkQueue使用的是LinkedBlockingQueue

ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor继承自ThreadPoolExecutor主要用来给定的延迟任务进行调度,与Timer类似,但是ScheduledThreadPoolExecutor使用DelayQueue来调度线程,更加灵活,功能更强大,具体就不解释了,想深入研究的可以去看看JDK源码,ScheduledThreadPoolExecutor对线程进行调度的具体实现方法,里面有英文的详细注释。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值