Java线程池源码分析

并发这块相当重要 ,并且初学的时候很难理解是真的,犹记得我在初学并发的时候(当然也不是很久之前),头铁的看了《Java并发编程实战》这本神书。但是由于是刚入门并发这块,所以,读这本书的过程是相当痛苦的,虽然痛苦还是坚持读了下去,虽然一知半解,但总算感觉摸到了门槛。

在看这本书的时候,对线程池这块印象确实比较深,所以决定走一波源码,而且线程池这块也是面试的重点之一吧,去读它的源码对理解线程池很有帮助。

基本概念

我这里先告知一些应该知道的基本概念:

  1. 我们向线程池提交的都是一个个任务,这些任务都是实现了Runnable 接口的,Runnable的run方法是没有返回值的。而线程池是可以获取线程执行的结果的,返回的是Future。因此,线程池其实是对提交的任务进行了包装了的,线程池将Runnable包装成Callable(Callable和Runnable的区别在于Callable是有返回值的),再将Callable包装成FutureTask,而FutureTask继承自 Future,Future可以用来表示一个任务的生命周期。
  2. 线程池还使用了阻塞队列(在下面的源码分析中叫工作队列)来进行任务的排队,队列一般分为有界队列,无界队列,和同步移交(Synchronous Handoff)。使用的比较多的是ArrayBlockingQueue 和 LinkedBlockingQueue,这里提一句同步移交,一般对于非常大或者无界的线程池,可以通过SychronousQueue来避免任务排队,直接将任务从生产者移交给工作者线程,它不算是真正的队列,只是一种移交机制而已,因此,如果线程池中没有线程接收任务,且当前线程大小大于最大大小的时候,就会执行饱和策略。(关于同步移交的可能有点模糊,但是看完之后相信你就能理解的)

继承关系

在看具体的源码之前,照例来看看它继承关系:

Java线程池源码分析_第1张图片

一般来说,这个线程池的基石是Executor接口,这个模式基于生产者-消费者模式不过由于它只有一个方法 execute(Runnable runnable),因此实际上常用的是ExecutorService ,它也是一个接口,但是在Executor的基础上增加了许多方法。

ExecutorService

ExecutorService 的生命周期有三个状态: 运行、关闭和终止。

下面看看ExecutorService下常用的方法:

public interface ExecutorService extends Executor {
	// 平缓的关闭,不再接受新任务,同时等待已经提交给队列的任务完成。
    void shutdown();
    // 比较粗暴的关闭,会取消所有在运行的任务,那么肯定连队列中的任务也不会再执行。
    List<Runnable> shutdownNow();
    // 是否关闭。
    boolean isShutdown();
    // 是否终止。需要注意的是,必须先调用shutdown()和shutdownNow()才会返回true,否则是不会返回true的。
    boolean isTerminated();
    // 提交Callable的任务。
    <T> Future<T> submit(Callable<T> task);
    // 提交Runnable的任务,result是做完返回值的。
    <T> Future<T> submit(Runnable task, T result);
    // 提交Runnable任务。
    Future<?> submit(Runnable task);

}

AbstractExecutorService

下面再来看看AbstractExecutorService,这里也是只讲一下重要的方法:

public abstract class AbstractExecutorService implements ExecutorService {
	// 点进new FutureTask 会看见是先是包装成Callable再赋值给其中的callable变量。
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }
    
    // 将Runnable 包装成RunnableFuture(最开始的基本概念已经说了)。
	// 再调用上面的newTaskFor方法。
	// 下面的大都类似,就不说了。
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        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 类了,它继承自AbstractExecutorService,AbstractExecutorService继承自ExecutorService 。

线程池状态

在看源码之前,先了解线程池的几个状态:

  • RUNNING: 接受新任务以及处理等待队列中的任务。
  • SHUTDOWN: 不接受新任务,但是能处理等待队列中的任务。
  • STOP: 不接受新任务,也不处理等待队列中的任务,还要中断执行任务的线程。
  • TIDYING: 所有的任务已经终止了,workerCount(正在工作的线程数) 为0,这个状态就会过渡为TIDYING,将会运行 terminated()这个钩子方法。
  • TERMINATED: terminated() 运行完。

在ThreadPoolExectuor

	// COUNT_BITS 为29,为什么是29,因为int总共有32位,线程池用前面3位表示线程池状态,后面29存放线程数
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // 000 1...,五亿多。
    // 表示的是线程池能创建的最大线程数,一般也不会达到这个数量级。
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // 线程池的各个状态码 后面29位都是0的
    // 111 0...
    private static final int RUNNING    = -1 << COUNT_BITS;
    // 000 0...
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    // 001 0...
    private static final int STOP       =  1 << COUNT_BITS;
    // 010 0...
    private static final int TIDYING    =  2 << COUNT_BITS;
    // 011 0...
    private static final int TERMINATED =  3 << COUNT_BITS;

从上面可以看见,这里注意一下,RUNNING是负数,SHUTDOWN是0,其它的都是大于0的。

然后获取线程池当前的状态和线程数是这样的:

// 将整数 c 的低 29 位修改为 0,就得到了线程池的状态
private static int runStateOf(int c)     { return c & ~CAPACITY; }
// 将整数 c 的高 3 为修改为 0,就得到了线程池中的线程数
private static int workerCountOf(int c)  { return c & CAPACITY; }

状态之间的转变如下:

  • RUNNING -> SHUTDOWN:调用shutdown()方法,也可能隐含在finalize()中。
  • (RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()方法。从这里也可以看出这也是上面ExecutorService讲shutdownNow()和shutdown()区别。
  • SHUTDOWN -> TIDYING:当队列和线程池是空的时候。
  • STOP -> TIDYING:当线程池空的时候。
  • TIDYING -> TERMINATED:当terminated()这个钩子方法执行完成。

源码分析

看完了上面的线程池状态讲解,下面正式来读源码,相信看到这里的你已经算是比较有耐心的了。

这次不从变量和构造方法来讲解,而是从常见的新建一个线程池入手。首先,很多人会用Executors这个工具类来创建线程池,一般是这样的:

Executors.newSingleThreadExecutor();
Executors.newFixedThreadPool();

当然还有其他的类型,就不列出来了。当我们点进去看的时候,就会出现下面的:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
public static ExecutorService newSingleThreadExecutor() {
		// 这个被FinalizableDelegatedExecutorService再次封装,是不允许修改里面的参数的意思。
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

这两个都指向了ThreadPoolExecutor的构造方法。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
}
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;
    }

上面的这些参数基本上就是线程池的核心变量了,下面讲一下这些变量。

变量 作用
corePoolSize 核心线程数,一直存活在线程池中,除非allowCoreThreadTimeOut为true,否则即使它们是空闲的, 也不回收。
maximumPoolSize 最大线程数,线程池运行的最大的线程数。与上面CAPACITY是有区别的。
keepAliveTime 存活时间,当线程池中的线程数大于核心线程数,且超过这个时间了还没任务做,那么该线程将会被终止。
unit 存活时间的单位
workQueue 工作队列,在前面基本概念中提了。
threadFactory 线程工厂,顾名思义,就是生产线程的。一般建议用一个带名字的。
handler 饱和策略,一般当线程池中线程数量达到最大线程数,且工作队列也满了的时候,会执行饱和策略。

execute方法

下面看看最核心的execute方法了:

public void execute(Runnable command) {
		// 如果提交的任务为null,抛出空指针异常。
        if (command == null)
            throw new NullPointerException();
        // 这个就是上面说的结合 线程池状态和线程数的 整数
        int c = ctl.get();
        // 获得线程池中的线程数,如果小于核心线程大小。
        if (workerCountOf(c) < corePoolSize) {
        	// 下面会有详细解释,这里大概解释一下。
        	// 线程池新建一个线程,并将任务提交给这个线程,并成功启动,就会返回true。
        	// 为false就表示不允许提交任务。
        	// 如果第二个参数的作用表示界限,为true表示以核心线程数为界限,false则是以最大线程数为界限。
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 到这里说明当前线程大小大于核心线程数,或者提交任务失败。
		
		// 如果线程池状态是RUNNING,并且向工作队列添加任务成功。
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            // 再次检查线程池状态,如果不是RUNNING了,那么执行拒绝策略。
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0) // 如果还是RUNNING,且线程池中没线程。
                addWorker(null, false);
        }
        // 如果不是RUNNING状态,或者工作队列满了,就执行这一步。
        // 使用maximumPoolSize为界限再试一次addWorker方法,如果还是失败,那么执行饱和策略
        else if (!addWorker(command, false))
            reject(command);
    }

可能乍一看上去有点绕,梳理一下:

  1. 我们通过execute提交任务,如果当前线程池中的线程数小于核心线程数,那么以核心线程数为界新建线程来处理这个任务,成功就没什么可说的了,失败就表示可能出现了点变化。
  2. 如果不小于核心线程数,或者上一步失败了。那么如果线程池状态还是运行的,将任务放到工作队列中去,如果放进去之后发现线程池状态改变了,那么将刚放进去的任务移出,然后执行饱和策略。如果线程池状态没改变,且当前工作线程数为0,那么新建线程。
  3. 如果线程池状态不是运行状态或者工作队列满了,那么再以最大线程数为界新建线程来处理这个任务,成功就没什么可说的了,失败就执行饱和策略。

addWorker方法

现在看下addWorker(Runnable firstTask, boolean core)方法是如何新建线程的:

	// firstTask表示的是任务,core为true绑定的是核心线程大小,为false绑定的是最大线程大小。
	private boolean addWorker(Runnable firstTask, boolean core) {
		// 这是一个跳出符,类似goto,不知道的可以谷歌一下。
        retry:
        for (;;) {
            int c = ctl.get();
            // 获得线程池的状态。
            int rs = runStateOf(c);
			// 这个if的逻辑可能有点难理解,但是仔细想想也还好,满足下面的条件之一就行。
			// 1.线程池的状态大于SHUTDOWN(最开始的时候也说了,RUNNING状态是负数的)。
			// 2.如果线程池状态为SHUTDOWN,提交的任务不为null。
			// 3.如果线程池状态为SHUTDOWN,提交的任务为null,工作队列为空。
			// 这里简单的分析下上面的情况:
			// 1.线程池状态大于0,那肯定不是RUNNING和SHUTDOWN状态了吗,return false完全没毛病。
			// 这里重复一下,SHUTDOWN状态不接受新任务,但是会处理工作队列中的任务。
			// 2.SHUTDOWN状态时,任务不为null,肯定是要return false的。
			// 3.任务为null,工作队列也是空的,肯定是返回false的。
			// 引申一下,如果状态为SHUTDOWN,工作队列不为null还是会新建线程的,原因看SHUTDOWN的意义。
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;
            // 到这里说明上面的if判断为false,也就是没有满足上面的条件。
            for (;;) {
            	// 获得当前的线程池中的线程数。
                int wc = workerCountOf(c);
                // 如果当前线程数大于CAPACITY
                // 或者core为true时大于核心大小,为false时大于最大大小,返回false。
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // 将那个整数值加一!说白了就是线程数+1,这里使用的是cas,成功就跳出循环,失败就走下面。
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                // 再次获得这个整数。
                c = ctl.get();
                // 如果和最开始的运行状态不一致,那么再从for来一遍。
                if (runStateOf(c) != rs)
                    continue retry;
            }
        }
        // 都到这里了说明一个return都没发生,跳出了for循环,那个整数+1成功了。
        
        // 线程是否开始工作
        boolean workerStarted = false;
        // 是否有将work添加到workers 中。
        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 {
                    
                    int rs = runStateOf(ctl.get());
                    // 状态为RUNNING或者为SHUTDOWN时,不接受新的任务,处理工作队列,这个上面说过。
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        // 检查这个新建线程是不是已经启动了,启动了抛异常。
                        if (t.isAlive()) 
                            throw new IllegalThreadStateException();
                        // 将新建的work添加到workers中,是一个HashSet。
                        workers.add(w);
                        // largestPoolSize记录线程池中有过线程数的最大值。
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;  
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                // 如果已经将worker添加到了workers中,启动这个worker。
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
        	// 如果线程没启动,那么做一些回退动作。
        	// 将worker从workers去掉,将之前+1的线程数减掉,试着将状态调到TERMINATED。
            if (! workerStarted)
                addWorkerFailed(w);
        }
        // 返回线程是否启动成功。
        return workerStarted;
    }

这个方法看上去也有点绕,但是只要记住其作用核心是新建线程就行了。

Worker类

这里看下Worker类(只列出变量、构造方法和run方法):

 	private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        final Thread thread;
        
        // 如果新建线程的时候同时指定了任务,那么新建的线程第一个执行这个任务(所以叫firstTask)
        // 当然这个任务也可以为null。
        Runnable firstTask;
        
        // 存放这个线程完成的任务数量,使用了volatile修饰。
        volatile long completedTasks;

        // 传入任务,
        Worker(Runnable firstTask) {
            setState(-1); 
            this.firstTask = firstTask;
            // 使用线程工厂来创建一个线程。
            this.thread = getThreadFactory().newThread(this);
        }
        public void run() {
            runWorker(this);
        }
        
    }

从Worker源码可以看出,这个类继承自AQS(可以说是无处不在的AQS,并发的基石)。其实从这里可以知道,线程池中的线程都包装成了Worker(译作工人)。话说Doug Lea大神的形容真传神,处理任务(Runnable)的是工人(Worker)。,上面那个addWorker,译作添加工人也完全形象。

runWorker 方法

我们知道,在addWorker方法中将新建线程启动会调用Worker中的run()方法, 它再调用runWorker(this),那么看一下这个runWorker方法:

	// 不断的从队列中获取任务并执行,同时解决一些问题。
	final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        // 新建线程传递的那个任务,也可以为null。
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        // 标识符。
        boolean completedAbruptly = true;
        try {
        	// 一直调用getTask获取任务去执行。
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // 如果线程池状态是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) {
                    	// 除了运行时异常的异常不让抛,弄成error抛出去。
                        thrown = x; throw new Error(x);
                    } finally {
                    	// 同beforeExecute作用类似,在运行任务结束后执行。
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    // 该线程完成的任务数++。
                    w.completedTasks++;
                    w.unlock();
                }
            }
            // 到这里说明完整的执行了一个任务,没有抛出异常。
            completedAbruptly = false;
        } finally {
        	// 到这里了说明getTask()拿不到任务了或者任务执行的过程中发生异常。
        	// 如果是拿不到任务了,getTask()自会将workCount - 1 的
        	// 如果是抛异常,那么执行这个方法将workCount - 1
            processWorkerExit(w, completedAbruptly);
        }
    }

这个方法很好理解,不解释。

getTask 方法

下面看看getTask():

	// 阻塞直到获取到任务然后返回。
	// 也会返回null,返回null的情况为以下几种:
	// 1.已经有大于maximumPoolSize的线程数了
	// 2.线程池的状态为STOP,或者为SHUTDOWN,且工作队列为空。
	// 3.线程等待keepAliveTime 时间了,线程要回收了,
	//      但是如果目前线程数小于corePoolSize则不会,除非allowCoreThreadTimeOut设为true。
	private Runnable getTask() {
		// 表超时。
        boolean timedOut = false; // Did the last poll() time out?

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

            // 这里可以分为两种情况。
            // 1. 状态大于等于STOP。
            // 2. 状态为SHUTDOWN,且工作队列为空。 
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            	// 通过cas将工作线程数-1.
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // 如果allowCoreThreadTimeOut 为true(前面说过,允许回收核心线程数内的线程)
            // 或者工作线程数大于核心线程数,那么就会超时关闭了。
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            
            // 意图是让工作线程数-1,if只要满足下面条件之一。
            // 1. 工作线程大于最大线程数。
            // 2. timed && timedOut为true,且 工作线程数大于1或者工作队列为空。
            // 讲解一下条件2。
            // timed为false时,意味着当前工作线程数小于核心线程数,不管timedOut超时标志是不是true,if都为false。
            // 假设timed为true,且timedOut超时标志为true,只要工作线程大于1或者工作队列为空了(回收最后一个线程)
            // 核心只要记住一句话。
            // 如果allowCoreThreadTimeOut为true,回收所有空闲超时线程,否则,回收大于核心线程数的空闲超时线程。
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
            // 到这里就是 工作线程数小于等于maximumPoolSize 且 没有超时
            try {
            	// workQueue 中获取任务
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
            	// 如果捕获到中断异常,那么重头再来一次。
                timedOut = false;
            }
        }
    }

看到这里基本上差不多了。

饱和策略

最后讲一下execute(Runnable command) 方法中可能会执行的reject(command);也就是饱和策略了,这里就不讲具体源码了,带着讲一下:

ThreadPoolExecutor 中有四个已经写好了的饱和策略,一般就用这四个。

  1. AbortPolicy(中止策略):会抛出未检查的RejectedExecutionException 异常,调用者可以捕获这个异常,然后根据需求编写处理代码。它也是默认的饱和策略。
  2. DiscardPolicy (抛弃策略):直接抛弃该任务,不做任何处理的。
  3. DiscardOldestPolicy (抛弃最旧策略):如果线程池状态不为SHUTDOWN,将会抛弃下一个将被执行的任务,然后再把这个任务提交了。(这个操作有点骚气)
  4. CallerRunsPolicy (调用者运行策略):是一种调节机制,既不会抛弃任务,也不抛出异常, 而是将任务回退给调用者,如,主线程中调用execute方法,如果执行饱和策略后,将该任务的运行提交给主线程去执行,使得一定时间内,主线程无法提交任务。

Executors下四种线程池

先看一下newFixedThreadPool,它的作用是创建出固定大小的线程池,因此可以看到它核心线程数和最大线程数是一样的,keepAliveTime 为0,因为最大线程数和核心线程数大小一样,所有这个没必要设置,使用LinkedBlockingQueue有界队列作为工作队列。

这样就可以分析一下,首先在线程池中线程数没有达到nThreads之前,一直是新建线程,达到了就将任务放置在LinkedBlockingQueue队列中,该队列满了就执行饱和策略。如果有线程发生了未预期的异常结束了,那么将补充一个线程。

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

newSingleThreadExecutor,它创建只有一个线程的线程池,除了将newFixedThreadPool中的nThreads改为1,其他都一样。
它主要是确保依照任务在队列中的顺序串行执行。

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

newCachedThreadPool,将创建一个可缓存的线程池,如果当前规模超过了处理需求时,将回收空闲的线程,需求增加时,则添加新线程,没有规模限制(其实还是有的,Integer.MAX_VALUE,不过肯定不会创建这么多线程)。它的工作队列设为SynchronousQueue,其作用在基本概念那里就已经说了。

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

newScheduledThreadPool,创建一个固定长度的线程池,而且以延迟或定时的方式来执行任务。这个由于没怎么接触过,所以就不多说了。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

总结

哇,经过冗长冗长的一段,终于到了总结这里。我并没有将全部的方法贴过来,毕竟贴完我感觉就要gg了,只是贴了核心部分,但是这也够喝一壶了。其实,并没有什么好总结的…毕竟要说的都在代码里,感情深,一口闷!但还是略微的总结一下吧。

  • 什么时候新建线程,首先,1. 如果线程池中工作线程数小于corePoolSize,那么提交任务时创建一个线程。2. 如果已经达到了corePoolSize数,那么将任务放置在workQueue中,这个时候是不会创建任务的。3.当队列满的时候,再提交任务,且工作线程数不大于maximumPoolSize,就会创建线程。
  • 什么时候回收线程,一句话,如果allowCoreThreadTimeOut为true,那么回收所有空闲超时线程,否则,回收大于corePoolSize的空闲超时线程。
  • 什么还是执行饱和策略,1. 当队列满了,且工作线程数已经大于等于maximumPoolSize了(此时肯定是大于等于corePoolSize的),那么执行饱和策略。2.工作线程数已经达到了 corePoolSize,任务放到workQueue中去,但有个线程已经将线程池关闭,且关闭线程池时没有将这个任务移出,那么执行饱和策略。有关这一块可以在execute()方法中看见。

最后,谢谢观看。本人才疏学浅,如有错误之处,欢迎指正,共同进步。

你可能感兴趣的:(java,java并发)