【Java并发】 - ThreadPoolExecutor详解

本文详细解析了Java中的线程池实现原理,包括ThreadPoolExecutor的构造参数、工作流程、状态设计等内容,并对比了不同线程池类型的适用场景。

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

概述

在编写并发应用的时候通常都可以使用线程池,因为比起我们自己手动的去创建新的线程并去开启线程线程池能够通过它自身对于线程的生命周期的管理来完成线程的复用这样就减少了反复创建线程的开销降低了资源的消耗。
在Java中Executor接口中只定义了一个简单的功能,它能够执行传来的Runnable的任务。
public interface Executor {
    void execute(Runnable command);
}
同时ExecutorService接口继承了Executor接口,定义了一些对于线程的生命周期管理的方法shutdown,shutdownNow等。而AbstractExecutorService又实现了ExecutorService接口实现了一些线程池基本的行为,如submit,newTaskFor,invokeAll等等。最后由ThreadPoolExecutor类继承AbstractExecutorService类来完成线程池的功能。虽然经常会使用Executors来工厂式的创建线程池,同时Executors也给了我们丰富的选择,但是个人还是推荐使用ThreadPoolExecutor来创建(理由后面讲)。

一个ThreadPoolExecutor的简单使用的例子:
public class ThreadPoolExecutorDemo {

	public static void main(String[] args) {
		
		BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
		
		ExecutorService executor = new  java.util.concurrent.ThreadPoolExecutor(3, 5, 0, TimeUnit.MILLISECONDS, workQueue);
		
		for(int i = 0; i < 10; i++){
			executor.execute(new MessageTask(i));
		}
	}

	static class MessageTask implements Runnable {

		private int messageId;
		
		public MessageTask(int messageId){
			this.messageId = messageId;
		}
		
		public void run() {
			
			System.out.println("send message start : " + messageId);
			
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			System.out.println("send message end : " + messageId);
		}
	}
	
}
这里的执行结果:
send message start : 2
send message start : 0
send message start : 1
send message end : 2
send message end : 1
send message end : 0
send message start : 4
send message start : 3
send message start : 5
send message end : 5
send message end : 4
send message end : 3
send message start : 8
send message start : 7
send message start : 6
send message end : 7
send message end : 6
send message end : 8
send message start : 9
send message end : 9

ThreadPoolExecutor的工作流程

构造函数参数

ThreadPoolExecutor的构造函数允许最多传入7个参数

  • int corePoolSize。核心线程池大小,当通过execute或者submit方法提交任务的时候会创建对应的线程执行任务,如果调用了prestartCoreThread那么会首先创建corePoolSize个线程等待任务的到来并执行。在任务不“过盛”的情况下线程池将保持corePoolSize个线程。
  • int maximumPoolSize。最大线程池大小,线程池允许的最大的线程个数。
  • long keepAliveTime。当线程数量大于corePoolSize时,corePoolSize以外个数的线程在新的任务到来之前的等待时间,超过时间则终止该线程。
  • TimeUnit unit。keepAliveTime的时间单位。
  • BlockingQueue<Runnable> workQueue。工作队列,在corePoolSize个线程都没有空闲时通过execute或者submit提交的任务将会保存到工作队列中。
  • ThreadFactory threadFactory。线程工厂,可以传入自定工厂来创建线程。
  • RejectedExecutionHandler handler。饱和策略,工作队列满了并且线程池的线程个数达到了maximumPoolSize的时候新提交的任务将交由handler决定如何处理。默认直接抛出异常.


线程的工作流程图:



在看看任务提交之后的执行顺序




因此假设核心线程数为3最大线程数为5工作队列的边界为5,那么连续提交15个任务的话应该是下面的流程(假设任务执行时间比较长):
1,2,3号任务将直接创建核心线程并执行。
4,5,6,7,8号任务将被放入工作队列。
9,10号任务将继续创建两个线程执行并且线程数达到最大线程数。
11-15号任务由于饱和抛出异常(默认)。


工作队列

一般常用的任务队列有四种:
  • ArrayBlockingQueue。基于数组的FIFO队列,因为数组是需要初始化大小的,所以是有边界的。
  • LinkedBlockingQueue。基于链表的FIFO队列,默认是无限大的(Integer.MAX_VALUE,也可以指定容量),当无限大的时候构造参数的maximumPoolSize将没有意义,同时也不会触发饱和策略。
  • SynchronousQueue。一个不会存放任何元素的队列,该队列会尽量将入队的任务直接转交出去,也就是当一个任务入队就必须等待它出队才能继续提交。
  • PriorityBlockingQueue。优先级的队列。不怎么常用

饱和策略

ThreadPoolExecutor提供四种饱和策略:
  • AbortPolicy。直接拒绝,将会抛出RejectedExecutionException
  • DiscardPolicy。丢弃。
  • DiscardOldestPolicy。丢弃当前任务队列中最老的任务并将当前任务加入队列
  • CallerRunsPolicy。由调用者执行。

Executors工厂的几种基于ThreadPoolExecutor的线程池

Executors工厂(跟Excutor接口没什么关系)提供了创建几种类型的ThreadPoolExecutor线程池的静态方法

newFixedThreadPool

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

该方法传入一个int,然后通过ThreadPoolExecutor的构造函数返回了一个ExecutorService接口的实例。
可以看到其中核心线程数和最大线程数都是用的传入的int参数,同时keepAliveTime为0,工作队列使用的是LinkedBlockingQueue同时没有指定容量,也就是无限大。

由此可知FixedThreadPool线程池的工作线程永远都是固定的,同时工作队列是无界的,这就意味着如果生产者(提交任务的线程)速度过快,那么结果就是不断的往工作队列中put任务,直到耗尽服务器资源。所以一般对于任务量是固定的(不会源源不断的生产)或者生产者提交速度不快的场景。

newSingleThreadExecutor

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

看名字就知道应该是一个只有一个工作线程的线程池。这个线程池同样也是使用的无界的LinkedBlockingQueue,这意味着同样面临这生产者过剩的问题。但是单一工作线程同样可以保证任务的执行顺序。


newCachedThreadPool

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


CachedThreadPool线程池的核心线程数为0,最大线程数为无限大,同时使用的是SynchronousQueue作为工作队列,这意味着当任务不断的提交的时候CachedThreadPool将会不断地去创建新的线程去执行提交的任务,同时如果任务数量减少了的话空闲的县城也将会在1分钟之后被回收,弊端也很明显,如果任务提交过快那么线程池中的线程数将会变得很多,随着线程数的增多带来的并不一定是效率的上升,过多的线程创建与回收以及上下文切换将会带来更多的开销。

因此个人觉得实际使用的时候还是直接显示的使用ThreadPoolExecutor来创建线程池,并且指定一个有界的工作队列,指定一个饱和策略(也可以通过实现RejectedExecutionHandler来完成自定义饱和策略如:写入日志或者序列化保存未完成的任务等)。


ThreadPoolExecutor源码解析


状态设计

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;

// 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;

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

这里状态设计采用了一个int型的变量,变量的前3位作为状态位,后29位作为线程的数量的计数使用。
COUNT_BITS = 29
CAPACITY:      0001 1111 1111 1111 1111 1111 1111 1111
RUNNING:       1110 0000 0000 0000 0000 0000 0000 0000
SHUTDOWN:      0000 0000 0000 0000 0000 0000 0000 0000
STOP:          0010 0000 0000 0000 0000 0000 0000 0000
TIDYING:       0100 0000 0000 0000 0000 0000 0000 0000
TERMINATED:    0110 0000 0000 0000 0000 0000 0000 0000


因为五个状态中只有running是小于0 的,所以可以直接通过是否小于0来判断是否处于running状态。并且另外四个状态的大小也依次上升,正好可以通过简单的大小判断来判断顺序。

属性

//任务队列
private final BlockingQueue<Runnable> workQueue;
//重入锁
private final ReentrantLock mainLock = new ReentrantLock();
//存放工Worker的容器HashSet
private final HashSet<Worker> workers = new HashSet<Worker>();
//Condition队列锁
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;

private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

这些属性基本上都可以通过命名来知道是干什么的。

Worker

Worker类继承自AQS,并实现了AQS中的方法,让Worker类具有了独占锁的功能同时实现Runnable接口
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
    private static final long serialVersionUID = 6138294804551838833L;
	//worker线程
    final Thread thread;
    //线程开始时执行的第一个任务,允许为null
    Runnable firstTask;
    //每个线程完成的任务数
    volatile long completedTasks;

    Worker(Runnable firstTask) {
        //将state设置为-1可以防止其他任何线程获取到当前worker的锁,因为获取到同步状态的条件为state>=0
		//因为现在worker的线程还没有真正的启动,还没有调用start方法
		setState(-1);
        this.firstTask = firstTask;
		//通过工厂创建线程
        this.thread = getThreadFactory().newThread(this);
    }

    //执行runWorker将this作为参数,它将会开启线程
    public void run() {
        runWorker(this);
    }

	//是否独占式的获取到了同步状态
    protected boolean isHeldExclusively() {
        return getState() != 0;
    }
	//尝试获取同步状态
    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }
	//尝试释放同步状态
    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

    public void lock()        { acquire(1); }
    public boolean tryLock()  { return tryAcquire(1); }
    public void unlock()      { release(1); }
    public boolean isLocked() { return isHeldExclusively(); }
	//如果线程启动了就中断线程
	//调用shutdowNow的时候会调用它来中断线程
    void interruptIfStarted() {
        Thread t;
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}


重要的方法

execute

源码:

public void execute(Runnable command) {
	//null则抛NPE异常
    if (command == null)
        throw new NullPointerException();
	//获取到当前的ctl(可以理解为记录运行状态和线程数量的一个state)
    int c = ctl.get();
	//如果当前线程(Worker)数量小于核心线程数量
	//那么直接尝试添加Worker(也就是增加工作线程,并且将当前这个任务作为first)
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
	//已经达到核心线程数量
	//如果线程池正在运行并且添加任务到任务队列成功
    if (isRunning(c) && workQueue.offer(command)) {
		//重新获取ctl
        int recheck = ctl.get();
		//如果线程池已经不在running状态那么从任务队列中移除当前任务并执行饱和策略
        if (! isRunning(recheck) && remove(command))
            reject(command); 
		//如果如果线程数量为0那么尝试创建一个线程
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
	//尝试创建非core的线程,失败说明线程数已经达到最大,那么执行饱和策略
    else if (!addWorker(command, false))
        reject(command);
}

execute的执行逻辑基本和前面给出的执行流程一致。

addWorker

源码:
//core为true说明创建核心线程,反之则为非核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        //如果运行状态不是running并且运行状态是SHUTDOWN或者firstTask为null或者任务队列为空则返回false
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

		//自旋
        for (;;) {
            int wc = workerCountOf(c);
			//如果线程数大于2^29 - 1或者线程数超过了最大限制(根据线程类型来决定)则返回false
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
			//尝试CAS方式添加Worker的数量,成功则退出外层循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
			//如果CAS失败或者运行状态由于线程数变化发生变化则继续cas尝试
            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 
		//构造一个Worker
        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());
				//如果运行状态为running或者 运行状态为shutdown的情况下firstTask为null则执行Worker的添加
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
					//这里的t如果是Alive状态那说明worker的线程在还没有执行start之前就已经提前开始了,所以抛出异常
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
					//添加到HashSet
                    workers.add(w);
                    int s = workers.size();
					//更新最大线程数
                    if (s > largestPoolSize)
                        largestPoolSize = s;
					//添加成功
                    workerAdded = true;
                }
            } finally {
				//释放锁
                mainLock.unlock();
            }
			//如果添加成功了则启动Worker线程
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
		//如果线程启动失败则做一个类似于Rollback的操作
		//将添加的worker从workers中移除
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}


runWorker

源码:
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
	//因为一开始的Worker中的同步状态为-1所以调用unlock方法从而使得state做一个加1的操作变成0,此时变得允许中断和获取到锁
    w.unlock(); // allow interrupts
	//用于判断任务是否正常执行完了(有可能异常返回等等)
    boolean completedAbruptly = true;
    try {
		//如果task不为空则执行
		//如果task为空那么就去调用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
            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);
    }
}
ThreadPoolExecutor提供了beforeExecute和afterExecute以支持扩展子类的时候可以在任务运行之前和运行之后(不论是否正常完成)可以做一些操作(写日志等)。


getTask

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

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

        //如果运行状态不为running并且 运行状态已经在stop阶段及以后了或者任务队列为空则不需要做将线程数减1
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        //是否需要超时
		//如果是非core线程或者设置了超时则需要
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
		
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
			//从任务队列中获取任务
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}


shutdown

源码:
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
		//检测是否有权限
        checkShutdownAccess();
		//更新状态为shutdown
        advanceRunState(SHUTDOWN);
		//中断线程
        interruptIdleWorkers();
		//留给ScheduledThreadPoolExecutor实现
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

shutdownNow
源码:
public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
		//更新状态位stop
        advanceRunState(STOP);
		//中断线程,这里会调用Worker的interruptIfStarted去中断
        interruptWorkers();
		//返回未完成的任务
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}

未完。。。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值