线程池学习

本文详细介绍了线程池的工作流程,包括当线程数少于corePoolSize时如何处理任务,以及当任务队列已满时如何处理。同时,讲解了线程池的创建参数如corePoolSize、maximumPoolSize、keepAliveTime等的作用,以及线程池的关闭方法和任务拒绝处理器。通过execute()和submit()方法提交任务,并分析了它们的区别。最后讨论了shutdown和shutdownNow方法的区别。

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

1.线程池处理任务流程:

在这里插入图片描述
ThreadPollExecutor执行execute()方法:
1)如果当前运行的线程少于corePollSize,则创建新的线程来执行任务(需要获取全局锁)
2)如果运行的线程数等于或者大于corePollSize,则将任务加入BlockingQueue。
3)如果BlockingQueue已满,则创建新的线程来处理任务(需要获取全局锁)
4)如果创建的新线程使得当前运行的线程数大于maximumPollSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

源码实现:

//command 可以是普通的Runnable 实现类,也可以是 FutureTask
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        // 获取ctl最新值赋值给c,ctl :高3位 表示线程池状态,低位表示当前线程池线程数量
        int c = ctl.get();
        //workerCountOf(c) 获取出当前线程数量
        //条件成立:表示当前线程数量小于核心线程数,此次提交任务,直接创建一个新的worker,对应线程池中多了一个新的线程。
        if (workerCountOf(c) < corePoolSize) {
            //addWorker 即为创建线程的过程,会创建worker对象,并且将command作为firstTask
            //core == true 表示采用核心线程数量限制  false表示采用 maximumPoolSize
            if (addWorker(command, true))
                //创建成功后,直接返回。addWorker方法里面会启动新创建的worker,将firstTask执行。
                return;
            // 添加worker失败,可能存在并发
            // 当前线程池状态发生了改变
            // RUNNING SHUTDOWN STOP TIDYING TERMINATION
            // 当线程池状态是非RUNNING状态时,addWorker(firstTask!=null, true|false) 一定会失败。
            // SHUTDOWN 状态下,也有可能创建成功。前提 firstTask == null 而且当前 queue  不为空。特殊情况。
            c = ctl.get();
        }
        // 1.当前线程数量已经达到corePoolSize
        // 2.addWorker失败..
    
    	//条件成立:说明当前线程池处于running状态,则尝试将 task 放入到workQueue中。
        if (isRunning(c) && workQueue.offer(command)) {
            //再次获取ctl保存到recheck。
            int recheck = ctl.get();
            //条件一:! isRunning(recheck) 成立:说明你提交到队列之后,线程池状态被外部线程给修改 比如:shutdown() shutdownNow()
            //这种情况 需要把刚刚提交的任务删除掉。
            //条件二:remove(command) 有可能成功,也有可能失败
            //成功:提交之后,线程池中的线程还未消费(处理)
            //失败:提交之后,在shutdown() shutdownNow()之前,就被线程池中的线程 给处理。
            if (! isRunning(recheck) && remove(command))
                //提交之后线程池状态为 非running 且 任务出队成功,走个拒绝策略。
                reject(command);
            //1.当前线程池是running状态
            //2.线程池状态是非running状态 但是remove提交的任务失败.

			//保证线程池在running状态下,最起码得有一个线程在工作。
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //1.入队失败
        //2.当前线程池是非running状态

		//1.入队失败.说明当前queue 满了!这个时候 如果当前线程数量尚未达到maximumPoolSize的话,会创建新的worker直接执行command
		//2.线程池状态为非running状态,这个时候因为 command != null addWorker 一定是返回false。
        else if (!addWorker(command, false))
            reject(command);
    }

工作线程:Worker:线程池创建线程时,会将线程封装成工作线程Worker,Worker在执行完任务后,还会获取工作队列里面的任务来执行。

//firstTask可以为null。为null 启动后会到queue中获取。
Worker(Runnable firstTask) {
    //设置AQS独占模式为初始化中状态,这个时候 不能被抢占锁。
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    //使用线程工厂创建了一个线程,并且将当前worker 指定为 Runnable,也就是说当thread启动的时候,会以worker.run()为入口。
    this.thread = getThreadFactory().newThread(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 {
            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);
        }
    }

线程池工作原理:
在这里插入图片描述
线程池中的线程执行任务分为两种情况:
(1)在execute()方法中创建一个线程时,会让这个线程执行当前任务
(2)这个线程执行完上图②的任务后,会反复从任务队列中获取任务来执行

2.线程池的使用

2.1 创建线程池:

    public ThreadPoolExecutor(
    	int corePoolSize, 
        int maximumPoolSize,
        long keepAliveTime,
        TimeUnit unit,
        BlockingQueue<Runnable> workQueue,
        RejectedExecutionHandler handler) {
    }
  • corePoolSize:核心线程数

    • 核心线程会一直存活,即使没有任务需要执行
    • 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
    • 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭
  • maximumPoolSize:最大线程数

    • 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
    • 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常
  • keepAliveTime:线程空闲时间

    • 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
    • 如果allowCoreThreadTimeout=true,则会直到线程数量=0
  • rejectedExecutionHandler:任务拒绝处理器

    • 当线程数已经达到maxPoolSize,任务队列已满,会拒绝新任务
    • 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
  • ThreadPoolExecutor类有几个内部实现类来处理这类情况:

    • AbortPolicy 该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
    • CallerRunsPolicy 该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
    • DiscardPolicy 忽视,什么都不会发生
    • DiscardOldestPolicy 踢出进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

2.2.向线程池提交任务:

execute()和submit()

  • execute()方法用于提交不需要返回值的任务,无法判断任务是否被线程池执行成功
  • submit()方法用于提交需要返回值的任务。线程池会返回一个Future对象,通过Future对象可以判断任务是否执行成功,并且可以通过future对象的get方法得到返回值,get方法会阻塞当前线程直到任务完成。

2.3.关闭线程池

可以调用线程池的shutdown或者shutdownNow方法关闭线程池。原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法中断线程,所以无法响应中断的任务可能无法终止。
shutdown和showdownNow的区别:

  • shutdownNOW首先将线程池的状态设置为STOP,然后尝试停止所有正在执行或者暂停的线程,并返回等待执行任务的列表
  • shutdown只是将线程池状态设置为SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TigRer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值