线程池

本文介绍了线程池,它可将任务添加到队列,创建线程后自动启动任务。使用线程池能重用线程、控制并发数、提供多种功能。还阐述了线程池类的继承关系,重点分析了ThreadPoolExecutor类,包括其构造参数、常用方法,最后介绍了Executors的几种线程池及执行流程。

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

线程池

线程池:

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

那么为什么要有线程池呢?
1.创建一个新的线程需要创建一个新的对象。使用线程池可以重用存在的线程,减少对象创建、消亡的开销。
2.线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者内存溢出。使用线程池可有效控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞。
3.线程池还提供了定时执行、定期执行、单线程、并发数控制等功能,方便使用。

了解了为什么要有线程池,接下来我们来看一下线程池类的继承关系:

从下面的源码可以知道ThreadPoolExecutor继承了AbstractExecutorService类

public class ThreadPoolExecutor extends AbstractExecutorService

AbstractExecutorService是一个抽象类,它实现了ExecutorService接口

public abstract class AbstractExecutorService implements ExecutorService

ExecutorService又是继承了Executor接口

public interface ExecutorService extends Executor

下面我们来看一下Executor类:

public interface Executor {
    void execute(Runnable command);
}

Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型。

再来看一下ExecutorService类

public interface ExecutorService extends Executor {
    void shutdown();
    List<Runnable> shutdownNow();
    boolean isShutdown();
    boolean isTerminated();
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
    <T> Future<T> submit(Callable<T> task);
    <T> Future<T> submit(Runnable task, T result);
    Future<?> submit(Runnable task);
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit) throws InterruptedException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

ExecutorService接口继承了Executor接口,并声明了一些方法:shutdown、shutdownNow、isShutdown、isTerminated、awaitTermination、submit、invokeAll、invokeAny。

再来看一下AbstractExecutorService抽象类:

public abstract class AbstractExecutorService implements ExecutorService {
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {...}
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {...}
    public Future<?> submit(Runnable task) {...}
    public <T> Future<T> submit(Runnable task, T result) {...}
        public <T> Future<T> submit(Callable<T> task) {...}
    private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,boolean timed, long nanos)
        throws InterruptedException, ExecutionException, TimeoutException {...}
    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException {...}
    public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                           long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {...}
    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException {...}
    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)
        throws InterruptedException {...}

AbstractExecutorService是一个抽象类实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法。

这是AbstractExecutorService提供的三个submit方法,在ThreadPoolExecutor中并未重写,但submit方法实际上还是调用的execute()方法。

    public Future<?> submit(Runnable task) {
    	//判断提交的任务task是否为null,若是null,则抛出空指针异常
        if (task == null) throw new NullPointerException();
        //用Future来获取任务执行结果
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        //调用execute提交任务的执行结果
        execute(ftask);
        //返回任务的执行结果
        return ftask;
    }

    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

ThreadPoolExecutor

ThreadPoolExecutor是线程池中最核心的一个类。我们来看一下ThreadPoolExecutor类的具体实现源码。

成员变量介绍:

	// ctl字段其实表示两个含义:runState和workerCount,高3位表示runState,低29位表示workerCount。
    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;
    //能接受新的任务且能处理队列里的请求
    private static final int RUNNING    = -1 << COUNT_BITS;
    //不能接受新的任务但是能处理队列里的请求
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    //不能接受新的任务、不能处理队列里的请求
    private static final int STOP       =  1 << COUNT_BITS;
    //所有的线程都已经terminated了,正准备调用terminated()方法
    private static final int TIDYING    =  2 << COUNT_BITS;
    //terminated()方法已经调用结束了
    private static final int TERMINATED =  3 << COUNT_BITS;
    //取ctl的高3位,获取runState(运行状态)
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    //取ctl的低29位,获取workerCount(worker的数量)
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    //把runState和workerCount合并成ctl,上面两个函数的反操作
    private static int ctlOf(int rs, int wc) { return rs | wc; }
    
	//任务缓存队列,用来存放等待执行的任务
	private final BlockingQueue<Runnable> workQueue;
	//线程池的主要状态锁,对线程池状态(比如线程池大小、runState等)的改变都要使用这个锁
	private final ReentrantLock mainLock = new ReentrantLock();
	//用来存放工作集
	private final HashSet<Worker> workers = new HashSet<Worker>();
	
	private final Condition termination = mainLock.newCondition();
	//用来记录线程池中曾经出现过的最大线程数
	private int largestPoolSize;
	//用来记录已经执行完毕的任务个数
	private long completedTaskCount;
	//创建新线程使用的工厂
	private volatile ThreadFactory threadFactory;
	//任务拒绝策略
	private volatile RejectedExecutionHandler handler;
	//超出核心线程数量以外的线程空余存活时间
	private volatile long keepAliveTime;
	//是否允许为核心线程设置存活时间
	private volatile boolean allowCoreThreadTimeOut;
	//核心线程的数量
	private volatile int corePoolSize;
	//最大线程数量
	private volatile int maximumPoolSize;
	

ThreadPoolExecutor构造方法:在ThreadPoolExecutor类中提供了四个构造方法,参数介绍如注释所示。

    public ThreadPoolExecutor(int corePoolSize,//核心线程的数量
                              int maximumPoolSize,//最大线程数量
                              long keepAliveTime,//超出核心线程数量以外的线程空余存活时间
                              TimeUnit unit,//存活时间的单位
                              BlockingQueue<Runnable> workQueue)//保存待执行任务的队列 {
                              ...
                              }

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) //创建新线程使用的工厂{
        					  ...
    						  }

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler)//任务拒绝策略{
        					  ...
    						  }

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        					  ...
    						  }

构造函数中各个参数的含义:

  • corePoolSize:核心线程的数量。当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
  • maximumPoolSize:最大线程数量。它表示在线程池中最多能创建多少个线程。
  • keepAliveTime:超出核心线程数量以外的线程空余存活时间。它表示当线程池中的线程数大于corePoolSize,线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用。
  • unit:存活时间的单位。参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
TimeUnit.DAYS;               //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒
  • workQueue:保存待执行任务的队列。它是一个阻塞队列,用来存储等待执行的任务。一般来说,这里的阻塞队列有以下几种选择:
ArrayBlockingQueue;基于数组、有界,按 FIFO(先进先出)原则对元素进行排序
LinkedBlockingQueue;基于链表,按FIFO (先进先出) 排序元素,吞吐量通常要高于 ArrayBlockingQueue
SynchronousQueue;不存储元素的阻塞队列 ,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQueue
PriorityBlockingQueue;具有优先级的、无限阻塞队列
  • threadFactory:创建新线程使用的工厂。主要用来创建线程。
  • handler:任务拒绝策略。表示当拒绝处理任务时的策略,有以下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 

ThreadPoolExecutor中常用的方法:

  • execute方法
    public void execute(Runnable command) {
    	//判断提交的任务command是否为null,若是null,则抛出空指针异常
        if (command == null)
            throw new NullPointerException();
                int c = ctl.get();
        //如果RUNNING的线程数目小于corePoolSize,直接调用addWorker方法启动一个新线程。
        //addWorker函数会检查runState和workerCount,如果不需要新建一个thread就会返回false了
        if (workerCountOf(c) < corePoolSize) {
			// addWorker有可能会失败,失败后重新获取状态并继续往下走
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //如果任务的状态为RUNNING,并且被成功的放入了workQueue,就进行双重检查
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //如果recheck不是RUNNING状态(也就是在isRunning(c)&&workQueue.offer中间并发发生了shutdown),就需要remove任务command,然后reject任务command。
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 如果是RUNNING状态但是没有可工作的线程,需要直接new一个
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //如果创建一个新worker失败了,说明线程池被关闭了或者饱和了(超过最大值了),就reject
        else if (!addWorker(command, false))
            reject(command);
    }

execute方法的执行分为三种情况:
1.当前线程池中线程数目小于corePoolSize,直接new一个thread;
2.当先线程池数据大于corePoolSize,则放入workQueue中;
3.如果workQueue满了且线程池中线程数小于maximumPoolSize,则new一个thread。

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

            //如果线程池的状态是除了RUNNING之外的状态就返回false,
            //但是要排除一种情况,线程池的状态是SHUTDOWN,但workers空了并且workQueue不空,
            //这种情况会调用addWorker(null,false)方法创建一个线程处理workQueue里的任务,这时不能直接返回false。
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                // 如果当前workers数目不小于CAPACITY或者不小于core选择的比较方式直接返回false
                //core是布尔类型的,当core为true时,用wc与corePoolSize比较。当core为false时,用wc与maximumPoolSize比较。
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //如果workerCount变化了,那么跳出retry循环
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  
                //如果runState变化了,那么重新继续retry循环
                if (runStateOf(c) != rs)
                    continue retry;
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            final ReentrantLock mainLock = this.mainLock;
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                mainLock.lock();
                try {
                    int c = ctl.get();
                    int rs = runStateOf(c);
// 再次检查线程池的状态,如果是RUNNING或者是SHUTDOWN但是firstTask不空,则把new出来的worker放入workers中。
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) 
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        //如果workers的大小大于largestPoolSize,更新largestPoolSize,将workerAdded置为true
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                // 创建worker成功后直接启动线程,workerStarted置为true
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
       		// 如果创建失败,要做清理操作
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
  • addWorkerFailed方法: 在addWorker方法中做清理操作
    private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
        	//如果创建的worker为空,把创建的worker从workers中remove
            if (w != null)
                workers.remove(w);
             //workerCount-1
            decrementWorkerCount();
            //尝试将线程池状态置为terminated
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }

runWorker方法:

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        //获取当前worker的第一个任务置于task变量中
        Runnable task = w.firstTask;
        //将worker对象的firstTask置为空,若不将worker对象的firstTask置为空,则firstTask引用指向的task对象将不会被gc回收
        w.firstTask = null;
        //将worker对象解锁
        w.unlock(); 
        boolean completedAbruptly = true;
        try {
            //进入while循环,如果task为null,将调用getTask()方法获取任务task对象
            //如果getTask()返回null则不进入循环
            while (task != null || (task = getTask()) != null) {
            	//如果进入循环则锁定worker对象
                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);
        }
    }

  • shutdown方法:
    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
       		// 检查调用者是否有权限执行shutdown
            checkShutdownAccess();
            // 将线程池的状态改成SHUTDOWN
            advanceRunState(SHUTDOWN);
            // 中断所有空闲的workers
            interruptIdleWorkers();
            onShutdown();
        } finally {
        	//释放锁
            mainLock.unlock();
        }
        // 尝试终止线程池
        tryTerminate();
    }

shutdown方法的执行步骤:把状态改成SHUTDOWN,中断所有空闲的workers,调用onShutdown钩子函数,最后调用tryTerminate尝试终止线程池。

  • shutdownNow方法:
    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
        	// 检查调用者是否有权限执行stop
            checkShutdownAccess();
            //将线程池的状态改成STOP
            advanceRunState(STOP);
            //中断所有的worker线程
            interruptWorkers();
            // 删除workQueue任务缓存队列里的待执行任务并返回任务列表
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        // 尝试终止线程池
        tryTerminate();
        return tasks;
    }

shutdownNow方法执行步骤:把状态改成STOP,中断所有的worker线程,删除workQueue里的任务,最后尝试终止线程池,返回workQueue里的任务列表。

  • advanceRunState方法: 将线程池的状态改成指定状态值
    private void advanceRunState(int targetState) {
        for (;;) {
            int c = ctl.get();
            if (runStateAtLeast(c, targetState) ||
                ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
                break;
        }
    }
  • awaitTermination方法:
    public boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (;;) {
                if (runStateAtLeast(ctl.get(), TERMINATED))
                    return true;
                if (nanos <= 0)
                    return false;
                nanos = termination.awaitNanos(nanos);
            }
        } finally {
            mainLock.unlock();
        }
    }

Executors的几种线程池

newFixedThreadPool: 用于负载比较重的服务器,为了资源的合理利用,需要限制当前线程数量。

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

核心线程数和最大线程数都是指定值nThreads,当线程池中的线程数超过核心线程数后,任务都会被放到阻塞队列中。
keepAliveTime 为 0,多余的空余线程会被立即终止,但这个线程池中没有多余的空线程。
阻塞队列是 LinkedBlockingQueue,使用的是默认容量 Integer.MAX_VALUE

这个线程池执行任务的流程如下:
1.线程数少于核心线程数时,新建线程执行任务
2.线程数等于核心线程数后,将任务加入阻塞队列
3.执行完任务的线程反复去队列中取任务执行

newSingleThreadExecutor: 用于串行执行任务的场景,每个任务必须按顺序执行,不需要并发执行。

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

SingleThreadExecutor是一种特殊的FixedThreadPool,它的核心线程数和最大线程数都是1。

这个线程池执行任务的流程如下:
1.线程池中没有线程时,新建一个线程执行任务
2.有一个线程以后,将任务加入阻塞队列。
3.新建的这一个线程不停地去队列里取任务执行

newCachedThreadPool: 用于并发执行大量短期的小任务,或者是负载较轻的服务器。

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

核心线程数为0
非核心线程数 Integer.MAX_VALUE
keepAliveTime 为 60 秒,超过后就会被回收。
阻塞队列 SynchronousQueue,这个队列的作用就是传递任务

这个线程池执行任务的流程如下:
1.没有核心线程,直接向 SynchronousQueue 中提交任务
2.如果有空闲线程,就去取出任务执行。如果没有空闲线程,就新建一个线程。
3.执行完任务的线程有 60 秒生存时间,如果在这个时间内可以接到新任务,就可以继续存活,否则就会被回收。

newScheduledThreadPool: 用于需要多个后台线程执行周期任务,同时需要限制线程数量的场景。

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值