浅谈线程池(ThreadPoolExecutor源码分析)

本文深入剖析了Java的ThreadPoolExecutor线程池实现,详细解释了构造方法、核心线程、任务调度策略及线程回收机制。通过源码分析,展示了如何降低资源消耗,提高响应速度和线程管理效率。同时,文中提到了线程池可能导致的内存泄漏问题及其原因,强调了正确使用shutdown方法的重要性。

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

浅谈线程池(ThreadPoolExecutor源码分析)

  1. 线程池的概念和优点

一个对线程统一管理资源类似于组件
优点就是降低了资源的消耗,通过重复利用已创建的线程,来减少创建销毁线程的消耗
提高了响应的速度,任务到了之后可以不等到线程创建之后才能执行
提高线程的可管理性,对线程进行统一管理

  1. 源码分析
//首先我们来分析线程池的构造方法
							//核心线程数
 	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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

	//这里是线程执行的主要逻辑
	public void execute(Runnable command) {
		//这里判断了是不是传入了runnable对象
        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))
                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);
    }


	// 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;
	//firstTask 执行的任务    core 是否使用核心线程执行 true是核心线程  false是非核心线程
	private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        //外层循环判断线程池状态
        for (;;) {
        	//获取正在运行线程数
            int c = ctl.get();
            //获取线程池运行状态
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            //这里就是线程池状态应该是shutdown和running状态
            //并且不能是已经终止队列中还有任务
            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;
        Worker w = null;
        try {
            w = new 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());
					//判断线程池状态是否为在运行
					//或者处于关闭状态并且任务为null
                    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;
    }


	 public void run() {
            runWorker(this);
        }
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
        	//这里判断就是传入的任务的有无,没有的话就从队列中拿出任务
        	//并且在获取的同时gettask()会将当前超过了核心线程数多余的线程关闭
            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
				//这里就是判断当前线程池的状态是stop状态
				//或者当前线程被中断且线程池状态是stop
				//并且当前线程没有被中断
                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);
        }
    }


	//启动所有核心线程轮询队列
	public int prestartAllCoreThreads() {
        int n = 0;
        while (addWorker(null, true))
            ++n;
        return n;
    }

这里要着重说一下工作线程,线程池在新建线程之后会将其封装成一个Worker对象,执行的时候是通过run循环获取队列中的任务执行
addWorker方法有4种传参的方式:
addWorker(command, true)
线程数小于corePoolSize。判断workers(HashSet)大小,如果worker数量>=corePoolSize返回false,否则创建worker添加到workers,并执行worker的run方法(执行firstTask并轮询tworkQueue);
addWorker(command, false)
线程数大于corePoolSize且workQueue已满。如果worker数量>=maximumPoolSize返回false,否则创建worker添加到workers,并执行worker的run方法(执行firstTask并轮询tworkQueue);
addWorker(null, false)
没有worker存活也就是任务梳理runcount为0,创建worker去轮询workQueue,长度限制maximumPoolSize。
addWorker(null, true)
在execute方法中就使用了前3种,结合这个核心方法进行以下分析
以上无论哪种方式都需要进行相关的ReentrantLock的加锁,所以效率和性能不会特别好。所以有了一个小办法,prestartAllCoreThreads()

  1. 线程回收

ThreadPoolExecutor回收工作线程,一条线程getTask()返回null,就会被回收。
分两种场景。
未调用shutdown() ,RUNNING状态下全部任务执行完成的场景
线程数量大于corePoolSize,线程超时阻塞,超时唤醒后CAS减少工作线程数,如果CAS成功,返回null,线程回收。否则进入下一次循环。当工作者线程数量小于等于corePoolSize,就可以一直阻塞了。
调用shutdown() ,全部任务执行完成的场景
shutdown() 会向所有线程发出中断信号,这时有两种可能。
所有线程都在阻塞
中断唤醒,进入循环,都符合第一个if判断条件,都返回null,所有线程回收。
任务还没有完全执行完
至少会有一条线程被回收。在processWorkerExit(Worker w, boolean completedAbruptly)方法里会调用tryTerminate(),向任意空闲线程发出中断信号。所有被阻塞的线程,最终都会被一个个唤醒,回收。

  1. 线程池问题

这里和大家说一个我之前犯过的问题,方法内创建线程池的时候并没有进行终止,导致内存泄漏,这里产生的原因就是上面说的,没有调用shutdown方法,会将线程数减少到核心线程数然后阻塞,这就导致了一直有引用,可达性分析一直可以扫描到导致的内存泄漏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值