Java线程池实现原理及源码分析

目录

一、前言

二、概述

1.优点

2.结构组成

三、原理分析

1.状态实现

2.阻塞队列

3.线程封装

四、源码分析

1.构造函数

2.执行线程入口

3.执行具体过程


一、前言

线程的重要性便不需要在这里多言。我们开发的时候其实一直在使用线程,只是这个过程被我们忽略了,Tomcat处理每个请求时使用的就是Java的多线程,一个请求即一个线程。

线程的生命周期有五个阶段:

  1. 新建(New):当程序实例化了一个Thread对象后,该线程就处于新建状态中,此时JVM仅为其分配内存并初始化了成员变量的值;
  2. 就绪(Runnable):当线程对象调用了start()方法后该线程就处于就绪状态,JVM会为其创建方法调用栈和程序计数器,等待调度运行;
  3. 运行(Running):就绪状态的线程获得了CPU时间片,开始执行run()方法的线程执行体;
  4. 阻塞(Blocked):当运行的线程CPU时间片消耗完时,便进入阻塞状态;
  5. 死亡(Dead):线程在run()方法执行结束后进入死亡状态,如果线程执行了interrupt()或者stop()方法,会以异常退出的方式进入死亡状态。

状态转换图:

 

一个线程的生命周期有如此多的状态变化,如果程序运行很多线程不停地重复着这五个生命周期,对于程序的性能消耗是非常大的,并且如果不控制线程的最大数量,也容易造成内存溢出的危险。而线程池就是为了解决这些个问题而诞生的。

二、概述

1.优点

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建、销毁线程造成的消耗;
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行;
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。

2.结构组成

结构图如下:

结构说明:可以很清楚的把这个分为两个部分:

  • 线程及线程池相关的接口:用来确定对开发者具体的方法,总体上确定线程池和线程的运行搭配方式;
  • 线程同步器相关:用来线程池实现阻塞队列和延迟队列等的线程实现类工具。

运行时流程图:

流程说明:可以看到其大致流程十分简单,无非是调用线程池的方法后根据方法来决定是否需要再次封装,封装后根据corePoolSize和线程池状态判断继续添加线程是OK的还是线程池出现了异常,如果OK则将其添加进阻塞队列或添加HashSet<Worker>进并运行线程,否则转换线程池状态,并判断是否拒绝执行线程或继续运行(简单只是相对的,Java在ThreadPoolExecutor类中只提供了顶层的方法和参数,具体的逻辑实现还需要子类去完成,因此搭配实现子类的不同逻辑和封装,才是线程池真正复杂的地方)。

三、原理分析

此次分析原理使用实例便是Java包concurrent实现的ScheduledThreadPoolExecutor类,这个类继承了ThreadPoolExecutor并自己实现了阻塞延迟队列DelayedWorkQueue。

1.状态实现

关于状态的几个参数以及判断方法:

参数说明:截取自ThreadPoolExecutor类的一段代码,分别说明几个参数以及方法的作用。

参数:

  • ctl:线程池控制状态以及当前运行线程数的参数,被AtomicInteger封装,其二进制的前面3位控制着线程池的五个状态状态,后面的29位控制着线程池的容量,使用位运算便可以轻松的使用一个参数完成两个功能;
  • COUNT_BITS:说明控制容量的位数,这里是29,因此线程最大容量为2^29次方,而前面3位则是用来控制状态;
  • CAPACITY:线程池的最大容量,值为2^29-1,二进制值为后面的29位全是1,因此使用该参数于上当前数量,便可获得线程池线程数量;
  • RUNNING:线程池处于正常运行状态,可以接收新的线程任务以及执行队列任务;
  • SHUTDOWN:将不会接收新的线程任务,但可以执行队列任务;
  • STOP:线程池已经停止,不会接收新的线程任务,执行队列任务也停止,并且正在执行的任务将会被暂停;
  • TIDYING:所有的任务都将被终止,运行数量为0,转为该状态时将会调用钩子方法terminated();
  • TERMINATED:钩子方法terminated()执行完成。

方法:

  • runStateOf方法:获得线程池的当前状态,使用当前的ctl值与上容量的反码,即前面三位是1,后面29位是0,相当于直接截取32位二进制的前面三位数值;
  • workerCountOf方法:获得当前线程池运行的线程数量,使用当前的ctl与上容量值即可完成截取32位2进制的后面29位值;
  • ctlOf方法:将要切换ctl值时所调用的方法,如ctl一开始的RUNNING状态初始化,TIDYING等状态的切花等;
  • runStateLessThan方法:判断参数c要小于s状态。由于五个状态分别代表了高位的-1,0,1,2,3,因此不同的状态存在大小比较,可以直接使用大于等于来判断;
  • runStateAtLeast方法:判断参数c大于等于s;
  • isRunning方法:判断当前ctl的值高位二进制是否是111,即ctl的值是否小于0。

八个参数状态具体的值以及二进制如下图:

个人感想:当时看到这个配置不由得想,为什么COUNT_BITS要取值为29,而不是取值为30或者28,Java线程池有五个状态,如果使用30,高位只能展示4种状态,而使用28,虽然可以展示更多的状态,但3位已经够用了(实际上还多出了3个状态),且取28线程池的容量将会减少一个平方。而五个状态为什么又分别取值为-1、0、1、2和3呢,很好理解,RUNNING取-1,直接判断高三位小于0即可判断,从深层次而言值小的状态都可以转变为值大的状态,而值大的状态无法转变为值小的状态。

2.阻塞队列

ScheduledThreadPoolExecutor类中实现了阻塞队列DelayedWorkQueue,其部分源码如下:

public class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor
        implements ScheduledExecutorService {
    static class DelayedWorkQueue extends AbstractQueue<Runnable>
            implements BlockingQueue<Runnable> {
        private static final int INITIAL_CAPACITY = 16;
        private RunnableScheduledFuture<?>[] queue =
            new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
        private final ReentrantLock lock = new ReentrantLock();
        private int size = 0;
        private Thread leader = null;
        private final Condition available = lock.newCondition();
        private void grow() {
            int oldCapacity = queue.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity < 0)
                newCapacity = Integer.MAX_VALUE;
            queue = Arrays.copyOf(queue, newCapacity);
        }
    }
}

集合说明:这个集合是基于堆实现的,默认容量为16,每次扩容都会扩大50%。且使用重入锁ReentrantLock来保证对集合操作是线程安全的。至于堆的实现这里便不做过多赘述。这个集合的作用便是用来存储任务线程,核心线程将一直从这里面获取任务线程来执行,如果一直没有则阻塞轮询,知道有任务线程进来,反反复复。

顺带提一下,ThreadPoolExecutor类中的HashSet<Worker>类型属性workers,这个集合存储的则是工作线程和其它非核心线程。集合常客便是核心线程,而其它的非核心线程则像过客一样来来去去。需要注意的是一个Worker同时只能处理一个任务线程。

3.线程封装

在ScheduledThreadPoolExecutor类中,私有内部类ScheduledFutureTask使用接口RunnableScheduledFuture封装了Runnable接口任务线程,接口源码如下:

public class FutureTask<V> implements RunnableFuture<V> {
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;
    private Callable<V> callable;
    private Object outcome;
    private volatile Thread runner;
    private volatile WaitNode waiters;
}

该类提供了一个异步可取消的计算类,除了提供Future类的基本方法外,还可以开始或取消计算、查看计算是否完成。一旦计算完成了,则不可以再调用或者计算,除非调用runAndReset方法。

该类也可以用来封装Callable和Runnable对象。

该类共有下列7种状态:

  1. NEW:新创建状态;
  2. COMPLETING:正在计算;
  3. NORMAL:正常计算结束;
  4. EXCEPTIONAL:计算异常;
  5. CANCELLED:取消计算;
  6. INTERRUPTING:正在暂停计算;
  7. INTERRUPTED:已经取消计算。

这几种状态的转换关系如下图:

这个类关键是用来搭配内部类DelayedWorkQueue来实现调度给定延迟之后运行某一线程,或者满足某种条件后进行取消或暂停。

四、源码分析

1.构造函数

关于线程池的源码分析是基于ScheduledThreadPoolExecutor类,其大致源码如下:

public class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor
        implements ScheduledExecutorService {
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory);
    }
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                   RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), handler);
    }
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory,
                                   RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory, handler);
    }
}

可以看出,其构造函数共有四种,每个都需要实例化的时候确定corePoolSize,并且最大线程池数量maximumPoolSize都是Integer.MAX_VALUE,即2^32-1,因此使用Java官方的线程池时,需要注意的是不能过多的创建线程数量,因为有些服务器的线程数往往还没达到2^32-1时,程序就已经内存溢出了。

2.执行线程入口

execute和submit方法的部分关键源码如下:

public class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor
        implements ScheduledExecutorService {
    public void execute(Runnable command) {
        schedule(command, 0, NANOSECONDS);
    }
    public Future<?> submit(Runnable task) {
        return schedule(task, 0, NANOSECONDS);
    }
    public <T> Future<T> submit(Runnable task, T result) {
        return schedule(Executors.callable(task, result), 0, NANOSECONDS);
    }
    public <T> Future<T> submit(Callable<T> task) {
        return schedule(task, 0, NANOSECONDS);
    }
    public ScheduledFuture<?> schedule(Runnable command,
                                   long delay,
                                   TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        RunnableScheduledFuture<?> t = decorateTask(command,
            new ScheduledFutureTask<Void>(command, null,
                                          triggerTime(delay, unit)));
        delayedExecute(t);
        return t;
    }
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                       long delay,
                                       TimeUnit unit) {
        if (callable == null || unit == null)
            throw new NullPointerException();
        RunnableScheduledFuture<V> t = decorateTask(callable,
            new ScheduledFutureTask<V>(callable,
                                       triggerTime(delay, unit)));
        delayedExecute(t);
        return t;
    }
    protected <V> RunnableScheduledFuture<V> decorateTask(
        Callable<V> callable, RunnableScheduledFuture<V> task) {
        return task;
    }
    private void delayedExecute(RunnableScheduledFuture<?> task) {
        if (isShutdown())
            reject(task);
        else {
            super.getQueue().add(task);
            if (isShutdown() &&
                !canRunInCurrentRunState(task.isPeriodic()) &&
                remove(task))
                task.cancel(false);
            else
                ensurePrestart();
        }
    }
}

execute和submit两个方法以及其重载方法是Executor接口和ExecutorService接口分别定义的,其差别无非是有没有返回值,这几个方法最终都会调用到schedule及其重载方法中,接着使用ScheduledFutureTask去封装Runnable线程以及过延迟时间,以实现延迟队列的功能。

3.执行具体过程

从上面的入口可以看到所有最终都会调用到ensurePrestart方法来,因此我们以这个方法源码开始慢慢的深入分析一下Java自己实现的延迟线程池是如何实现的,一些旁支末节或者看方法名称就知道其大致作用的便不做过多分析,部分源码如下:

public class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor
        implements ScheduledExecutorService {
    void ensurePrestart() {
        int wc = workerCountOf(ctl.get());
        if (wc < corePoolSize)
            addWorker(null, true);
        else if (wc == 0)
            addWorker(null, false);
    }
    private boolean addWorker(Runnable firstTask, boolean core) {
        // 重新调用点
        retry:
        // 开始外层循环,这层循环是为了持续的监听其它线程是否改变了线程池状态
        // 以及数量,并重新获得最新的ctl和状态值
        for (;;) {
            // 获取ctl的值以及获得线程池当前状态
            int c = ctl.get();
            int rs = runStateOf(c);
    
            // 判断工作队列是否为空,为空则返回
            if (rs >= SHUTDOWN &&
            !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
                return false;
    
            // 开始第二层循环,此层循环是为了当前线程在进行CAS操作失败时再次重试
            // 同时需要注意的是在此流程中如果core为true,wc<corePoolSize成立
            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // 使用CAS增加ctl的值,使其+1
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                // CAS操作失败
                c = ctl.get();
                // 如果池运行状态发生变化,则跳至第一层循环重新获取ctl值和状态
                if (runStateOf(c) != rs)
                    continue retry;
            }
        }
        
        // 线程执行到这里,说明当前线程获得了锁,并且ctl值已经+1
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            // 这里十分关键,将外部传来的firstTask线程使用内部类Work包装
            // 但是当前流程这里的firstTask对象为null
            // 需要注意的是Work中的thread线程封装的是它自己
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                // 加锁对workers当前运行线程集合进行操作
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    int rs = runStateOf(ctl.get());
    
                    // 只有RUNNING或者SHUTDOWN状态并且外部线程firstTask为null
                    // 才允许调用线程
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive())
                            throw new IllegalThreadStateException();
                        // 将Worker添加进HashSet<Worker>集合
                        workers.add(w);
                        int s = workers.size();
                        // 最大池数量增加
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    // 释放锁
                    mainLock.unlock();
                }
                if (workerAdded) {
                    // 如果前面的状态判断成功,则运t线程
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            // 如果运行失败,将workers集合的Worker删除,ctl值-1
            // 并且尝试终止线程池
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
}

关键的Worker内部类部分关键代码如下:

public class ThreadPoolExecutor extends AbstractExecutorService {
    private final class Worker
            extends AbstractQueuedSynchronizer
            implements Runnable {
        // 具体运行的线程,一般指的就是Worker对象
        final Thread thread;
        Runnable firstTask;
        volatile long completedTasks;
        Worker(Runnable firstTask) {
            setState(-1);
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
        public void run() {
            // 线程运行后调用到这里
            runWorker(this);
        }
    }
    final void runWorker(Worker w) {
        // 获得当前线程
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        // 运行此过程线程允许被中断,因为getTask需要和其它线程竞争
        w.unlock();
        boolean completedAbruptly = true;
        try {
            // 获得阻塞队列堆顶的线程以便等下执行
            // getTask方法也是很重要的,等下再分析
            while (task != null || (task = getTask()) != null) {
                // 重新加锁
                w.lock();
                // 当线程池的状态处于:STOP,TIDYING或者TERMINATED并且
                // 当前线程是可被中断的,并且wt线程未被中断,则将wt中断
                // 这样是为了:
                // 当线程池正在暂停,确保线程被中断;如果没有,确保线程未中断
                // 这样做的目的是调用shudownNow方法清理中断线程时的再校验
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    // 执行前的操作
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        // 执行线程的run方法
                        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 {
            // Worker运行完后将workers集合中的对象删除
            // 并将濒死的工作线程进行清楚和统计
            // 如果线程由于用户任务异常而退出
            // 或者运行的工作线程小于corePoolSize
            // 或者队列非空但没有工作线程,则可能终止池或替换工作线程。
            processWorkerExit(w, completedAbruptly);
        }
    }
}

可以看到Worker继承了AbstractQueuedSynchronizer类,即自定义锁的同步器,有兴趣可以看一下Java并发编程工具锁深入了解原理实现

runWorker方法中的getTask方法以及processWorkerExit方法流程源码:

private Runnable getTask() {
    boolean timedOut = false;

    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 如果线程池状态失效或者阻塞队列为空,则直接返回,并且数量减一
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            // 如果需要根据时间判断,则调用poll方法,否则直接拿取头节点
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            // 获取超时
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // 如果runWorker方法异常结束,则直接将数量-1
    if (completedAbruptly)
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 去除workder集合中的对象
        completedTaskCount += w.completedTasks;
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

    // 尝试终止线程池
    tryTerminate();

    int c = ctl.get();
    // 如果处于SHUTDOWN或者RUNNING状态下
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            // 判断是否有额外的线程需要处理,
            if (workerCountOf(c) >= min)
                return;
        }
        // 处理非核心线程
        addWorker(null, false);
    }
}

因此其逻辑核心便是确保核心线程和阻塞队列要被全部用完才会去创建非核心线程:

  • 如果当前活动线程数 < 指定的核心线程数,则创建并启动一个线程来执行新提交的任务(此时新建的线程相当于核心线程);
  • 如果当前活动线程数 >= 指定的核心线程数,且缓存队列未满,则将任务添加到缓存队列中;
  • 如果当前活动线程数 >= 指定的核心线程数,且缓存队列已满,则创建并启动一个线程来执行新提交的任务(此时新建的线程相当于非核心线程)。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值