java线程池的源码分析

本文详细剖析Java线程池的内部运作,包括核心参数、线程创建与回收机制、状态转换以及拒绝策略。通过源码解读,揭示线程池如何高效管理线程资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接上文

前面介绍了下线程池的使用方法,传送门:java线程池介绍,这里看下具体的源码

回顾下核心参数

流程

线程池的构造方法

/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
  • corePoolSize:线程池的核心线程数
  • maximumPoolSize:最大线程数,如果任务过多,超过核心线程数的处理能力,就会创建新的线程执行任务,这个参数,代表所能创建的最大线程的个数。
  • keepAliveTime:线程存活时间。在上面的情况中,如果创建的线程数量超过了核心线程数,并且在keepAliveTime时间段内,没有新的任务执行,就回把多余的线程进行回收。
  • unit:时间单位。
  • workQueue:任务队列。如果线程中的线程处理任务繁忙,此时任务就将堆积到任务队列里面,线程处理时,从队列里面取出执行
  • RejectedExecutionHandler:拒绝策略。如果使用了有界队列,任务达到了最大值,再有任务到达时,该怎么做呢?这时就涉及到拒绝队列

从默认线程的创建出发

默认的线程池创建

@Test
    public void cachedThreadPoolTest() {
        // 可缓存的线程池
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello world");
            }
        });
    }

这是jdk提供的一种默认线程池,那么返回的ExecutorService是会什么呢?

ExecutorService接口继承了Executor接口,Executor接口里面只有一个方法execute,参数是Runnable接口,说明这个方法是真正执行任务的。

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

那么既然有了execute方法,为什么还要定义一个ExecutorService接口呢?主要是ExecutorService实现了很多扩展功能,比如常用的提交任务submit(),关闭任务提交shutdown(),检测任务是否被关闭isShutdown()等

看下类图

当得到了创建的线程池后,提交了任务,任务怎么执行的呢?

任务执行

由上面的流程图可知,提交一个任务执行到线程池执行,如果核心线程未满,将直接获取一个线程执行任务。

也就是说,如果如果刚创建了一个线程池,在核心线程未满的情况下,都会新建一个线程来执行任务,我们看下代码execute

public void execute(Runnable command) {
        // 任务为空,抛出异常
        if (command == null)
            throw new NullPointerException();

        int c = ctl.get();
        // 如果工作线程数<核心线程
        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);
    }

 整个流程就是前面流程图所示。

重点看下添加任务线程,addWorker

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            // 线程池如果不是运行状态,不创建新的线程
            if (rs >= SHUTDOWN &&
                    ! (rs == SHUTDOWN &&
                            firstTask == null &&
                            ! workQueue.isEmpty()))
                return false;

            for (;;) {
                // 计算工作线程数量
                int wc = workerCountOf(c);
                // 超过线程容量,,不创建新的线程
                if (wc >= CAPACITY ||
                        wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // CAS方式递增线程计数器,+1
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        ThreadPoolExecutor.Worker w = null;
        try {
            w = new ThreadPoolExecutor.Worker(firstTask);
            // 真正的创建线程
            final Thread t = w.thread;
            if (t != null) {
                // 同步创建,显示锁控制
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                    // 再次检验线程池执行状态,防止在线程创建过程中,线程池已经被关闭
                    if (rs < SHUTDOWN ||
                            (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        // 添加进工作线程队列
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                // 线程创建成功,立即启动
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            // 如果启动失败,执行清理工作
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

看下大致逻辑:

  • 方法2个参数,addWorker(Runnable firstTask, boolean core):firstTask表示,新线程是否先运行的任务;core表示是否核心线程,如果为false,表示非核心线程
  • 如果线程池不是运行状态,则不创建新线程
  • CAS的方式递增线程计数器
  • 创建工作线程。这里并不是直接new Thread,而是通过包装的Worker创建
  • 如果创建成功,将线程放入工作线程队列,并且启动
  • 如果启动失败,则进行清理:将新线程中断,线程计数器递减,并将线程从工作线程移除

其中上面的线程启动失败的方法有3步

private void addWorkerFailed(ThreadPoolExecutor.Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 将任务从工作队列移除
            if (w != null)
                workers.remove(w);
            // 工作线程计数器递减
            decrementWorkerCount();
            // 中断线程
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }

这里有一点要说明的是,java中并没有提供直接停止线程的方法,一般都是采用中断的方式,这个在后面会分析到

线程池的状态

上面在创建线程的过程中,会判断线程池的状态,那么线程池有哪几个状态呢?

// 接受并执行新任务
    private static final int RUNNING    = -1 << COUNT_BITS;
    // 不接受新任务,但会执行任务队列中已存在的任务
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    // 不接受新任务,不会执行任务队列中已存在的任务,并且会中断正在执行的任务
    private static final int STOP       =  1 << COUNT_BITS;
    // 工作线程为0,并且任务都是terminated状态,到TERMINATED之前的过渡状态
    private static final int TIDYING    =  2 << COUNT_BITS;
    // 线束方法执行完成
    private static final int TERMINATED =  3 << COUNT_BITS;

其中的状态转换如下面的状态机所示

  • 如果executor执行shutdown(),状态会由RUNNING变成SHUTDOWN
  • 如果是执行shutdownNow(),状态会直接变成STOP
  • 如果线程池pool变为空,则会状态转换成TIDYING
  • 如果在TIDYING状态,对应的钩子方法terminated()执行完成,最终会变成TERMINATED

看下shutdown()方法,里面会将状态设置为SHUTDOWN:

public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 设置为SHUTDOWN状态
            advanceRunState(SHUTDOWN);
            // 中断空闲的线程
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }

再看下shutdownNow(),里面会将状态设置为STOP:

public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 设置状态为STOP
            advanceRunState(STOP);
            // 中断线程
            interruptWorkers();
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }

这2个方法的关键就在于interruptIdleWorkers与interruptWorkers的区别:shutdown()是中断未执行任务的线程

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();
        }
    }

而shutdownNow是将正在执行中的任务也进行中断:根据当前线程的状态来判定,如果是state>0,则表示正在执行,将进行中断

void interruptIfStarted() {
            Thread t;
            // 线程状态 >=0,并且未被中断
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }

看一个具体的例子:如果线程池执行了shutdown()或者shutdownNow()方法,这时线程池的状态不再RUNNING,会转换到SHUTDOWN或者STOP,此时再提交任务,则会执行相应的拒绝策略

public void cachedThreadPoolTest() {
        // 可缓存的线程池
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello world");
            }
        });

        // 关闭线程池,不再接受任务
        executor.shutdown();
//        executor.shutdownNow();
        // 再次提交任务
        executor.submit(() -> {
            System.out.println("hello world");
        });
    }

运行test程序,会抛出异常

看下submit()方法,除了返回Future之外,真正调用的也是execute()方法,这时候发现线程池状态变化,addWorker肯定返回false,进而执行拒绝策略

而默认的拒绝策略就是抛出异常,所以上面的程序会抛出异常

线程回收

前面介绍线程池核心参数时,有一个线程存活时间keepAliveTime,再看下它的作用

keepAliveTime:线程存活时间。在上面的情况中,如果创建的线程数量超过了核心线程数,并且在keepAliveTime时间段内,没有新的任务执行,就回把多余的线程进行回收。

那么线程池具体是怎样回收线程呢?

前面讲了,线程池是通过Worker-工作线程来创建的,然后启动线程

看下Worker类图,实现Runnable接口,则会覆写run()方法,线程启动,用于执行任务,里面又调用了runWorker

看下真正的runWorker()

final void runWorker(ThreadPoolExecutor.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 pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                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);
        }
    }

从上面看,主要是getTask()和processWorkerExit(w, completedAbruptly)决定是否回收的

private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

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

            // Check if queue empty only if necessary.
            // 如果线程池关闭,则返回null,将执行线程退出
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            // 是否超时由allowCoreThreadTimeOut标识true,或者超过核心线程
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            // 如果超时,则返回null,将执行线程退出
            if ((wc > maximumPoolSize || (timed && timedOut))
                    && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                // 如果允许超时,则执行带超时的方法从阻塞队列获取任务,反之,则由不超时的take()方法获取任务
                // take()不会超时,会一直阻塞队列直到取到任务
                Runnable r = timed ?
                        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                        workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

从上面可以看出,超时有2种情况

  • 是线程池关闭,将会当作超时对待,回收线程
  • 设置允许回收allowCoreThreadTimeOut为true,且从任务队列获取任务超时(这种主要是超时时间段没有可执行任务),也将进行回收(对core-max部分)

AQS

上面代码里面一起出现ReentrantLock及getState(),里面涉及到AQS,将在下一节中介绍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值