什么是线程池

在java中,线程的创建和销毁都需要消耗资源和时间,甚至可能要比在处理实际的用户请求的时间和资源要多的多。如果在JVM中创建了过多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。这时候就需要线程池。线程池的核心逻辑是提前创建好若干个线程放在一个容器中。如果有任务需要处理,则将任务直接分配给线程池中的线程来执行就行,任务处理完以后这个线程不会被销毁,而是等待后续分配任务。同时通过线程池来重复管理线程还可以避免创建大量线程增加开销。
合理使用线程池的优势:
1.降低了创建线程和销毁线程的开销。
2.提高了响应速度,执行新任务不需要等待线程创建就可以立马执行。
3.合理的设置线程池大小可以避免因为线程数超过硬件资源瓶颈带来的问题。

线程池的使用

public class TheradPoolDemo {
    public static ExecutorService pool = Executors.newFixedThreadPool(3);
    public static void main(String[] args) {
        for (int i=0;i<3;i++){
            pool.execute(new Thread(()->{
                System.out.println("线程:"+Thread.currentThread().getName()+"启动!");
            }));
        }
        pool.shutdown();
    }
}

Executors 里面提供了多个个线程池的工厂方法,常用的如下所示:

方法名

作用

newFixedThreadPool

创建一个指定线程数量的线程池,当有一个任务提交时,若线程池中有空闲线程,则立即执行,若没有,则会被暂缓在一个任务队列中,等待有空闲的线程去执行。

newSingleThreadExecutor

创建只有一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中

newCachedThreadPool

返回一个可根据实际情况调整线程个数的线程池,不限制最大线程数量,若有空闲的线程则执行任务,若无任务则不创建线程。并且每一个空闲线程会在 60 秒后自动回收

newScheduledThreadPool

: 创建一个指定线程数量的线程池,但是这个线程池还带有延迟和周期性执行任务的功能,类似定时器。

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    //ScheduledThreadPoolExecutor继承于ThreadPoolExecutor,所以本质上,还是基于ThreadPoolExecutor
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory);
    }

从代码中可以看出,这四种线程池都是基于ThreadPoolExecutor的。而ThreadPoolExecutor提供了多个重载的构造方法。下面是最完整的构造方法:

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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

创建线程池时的参数解析

创建一个线程池时需要输入几个参数,如下。
1)corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。
2)runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。可以选择以下几个阻塞队列。
·ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序。
·LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
·SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
·PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
3)maximumPoolSize(线程池最大数量):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如果使用了无界的任务队列这个参数就没什么效果。
4)ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。使用开源框架guava提供的ThreadFactoryBuilder可以快速给线程池里的线程设置有意义的名字,代码如下。

new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build();

5)RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。在JDK 1.5中Java线程池框架提供了以下4种策略。
·AbortPolicy:直接抛出异常。
·CallerRunsPolicy:只用调用者所在线程来运行任务。
·DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
·DiscardPolicy:不处理,丢弃掉。
当然,也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化存储不能处理的任务。
6)keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。
7)TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和纳秒(NANOSECONDS,千分之一微秒)。

线程池原理

线程池的主要处理流程

java 多线程 工厂模式 并发 java多线程并发编程 线程池_开发语言


ThreadPoolExecutor执行示意图

java 多线程 工厂模式 并发 java多线程并发编程 线程池_java_02


ThreadPoolExecutor执行execute方法分下面4种情况。

1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。

2)如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。

3)如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。

4)如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

ThreadPoolExecutor采取上述步骤的总体设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁(那将会是一个严重的可伸缩瓶颈)。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁。

源码分析

ThreadPoolExecutor中的常量属性的含义与二进制计算

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;  //32-3=29
    //将 1 的二进制左移 29 位,再减 1 表示最大线程容量
    //二进制为0001 1111 1111 1111 1111 1111 1111 1111
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

	//运行状态保存在 int 值的高 3 位 (所有数值左移 29 位)
    // runState is stored in the high-order bits
    
    // 接收新任务,并执行队列中的任务 
    //负数运算需要用补码。结果的原码:1010 0000 0000 0000 0000 0000 0000 0000 = -536870912
    private static final int RUNNING    = -1 << COUNT_BITS;  
    // 不接收新任务,但是执行队列中的任务 =0
    private static final int SHUTDOWN   =  0 << COUNT_BITS;  
    // 不接收新任务,不执行队列中的任务,中断正在执行中的任务 
    //0010 0000 0000 0000 0000 0000 0000 0000=536870912
    private static final int STOP       =  1 << COUNT_BITS;
    //所有的任务都已结束,线程数量为 0,处于该状态的线程池即将调用 terminated()方法 
    //0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824
    private static final int TIDYING    =  2 << COUNT_BITS;
    // terminated()方法执行完成
    //0110 0000 0000 0000 0000 0000 0000 0000=1610612736
    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; }
ctl高3位代表线程池状态,低29位代表线程池的数量
ctl初始值=RUNNING|0 = RUNNING补码 | 0
   = 1110 0000 0000 0000 0000 0000 0000 0000 | 0000 0000 0000 0000 0000 0000 0000 0000
   = 1110 0000 0000 0000 0000 0000 0000 0000
ctl原码=1010 0000 0000 0000 0000 0000 0000 0000
   
workerCountOf(c)表示当前工作线程的数量:
	c & CAPACITY = ctl补码 & CAPACITY
	=1110 0000 0000 0000 0000 0000 0000 0000 & 0001 1111 1111 1111 1111 1111 1111 1111
	=0000 0000 0000 0000 0000 0000 0000 0000
	
runStateOf(c)表示当前线程池的状态:
	c & ~CAPACITY = ctl补码 & ~CAPACITY
	=1110 0000 0000 0000 0000 0000 0000 0000 & 1110 0000 0000 0000 0000 0000 0000 0000
	=1110 0000 0000 0000 0000 0000 0000 0000
原码=1010 0000 0000 0000 0000 0000 0000 0000

ThreadPoolExecutor.execute

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        //workerCountOf(c)结果看上文二进制运算
        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);
        }
        //3.核心池已满,队列已满,试着创建一个新线程
        else if (!addWorker(command, false))
        	//如果创建新线程失败了,说明线程池被关闭或者线程池完全满了,拒绝任务
            reject(command);
    }

addWorker方法

private boolean addWorker(Runnable firstTask, boolean core) {
		//相当于goto
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);  //rs表示当前线程池的状态

            // Check if queue empty only if necessary.
            //如果线程处于非运行状态,并且 rs 不等于 SHUTDOWN 且 firstTask 不等于空且且workQueue 为空,直接返回 false(表示不可添加 work 状态)
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
            	//获取工作线程的数量
                int wc = workerCountOf(c);
                //core传入的是true.当工作线程数量大于,线程池的最大线程容量
                //或者工作线程数量大于核心线程数量。则返回false,不创建work线程
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //CAS操作ctl的值,如果=c,则改为c+1
                //实际上是增加工作线程的个数
                if (compareAndIncrementWorkerCount(c))
                    break retry; //中断并跳出retry所标记的循环
                c = ctl.get();  // Re-read ctl
                //如果失败,继续重试
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
		//上面这段代码主要是对 worker 数量做原子+1 操作,下面的逻辑才是正式构建一个 worker
        boolean workerStarted = false;  //工作线程是否启动的标识
        boolean workerAdded = false;  //工作线程是否已经添加成功的标识
        Worker w = null;
        try {
        	//将execute方法传入的Thread或者Runnable任务,构建一个worker对象
        	//实际上worker对象内包含一个新创建的线程
            w = new Worker(firstTask);
            final Thread t = w.thread;  //从 worker 对象中取出线程
            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任务为空,才能添加到workers 集合中
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        //任务刚封装到 worker 里面的线程,还没 start,线程就是alive
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);  //将新创建的 Worker 添加到 workers 集合中
                        int s = workers.size();                       
//如果集合中的工作线程数大于最大线程数,这个最大线程数表示线程池曾经出现过的最大线程数
                        if (s > largestPoolSize)
                            largestPoolSize = s;   //更新线程池出现过的最大线程数
                        workerAdded = true;  //表示工作线程创建成功了
                    }
                } finally {
                    mainLock.unlock();  //释放锁
                }
                if (workerAdded) {  {//如果 worker 添加成功
                    t.start();  //启动worker
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
            	//如果添加失败,工作线程数减一,并从workers移除掉刚刚添加的worker
                addWorkerFailed(w);
        }
        return workerStarted;
    }

Worker类

Worker类实现了Runnable方法。每个 worker,都是一条线程,同时里面包含了一个 firstTask,即初始化时要被首先执行的任务;最终执行的是Worker的run方法。firstTask 用它来保存传入的任务,thread 是在调用构造方法时通过 ThreadFactory 来创建的线程,是用来处理任务的线程。
Worker 继承了 AbstractQueuedSynchronizer类。使用 AQS 来实现独占锁的功能。作用是:
1、如果worker正在执行任务,则不应该中断线程;
2、如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断;
3、线程池在执行 shutdown 方法或 tryTerminate 方法时会调用 interruptIdleWorkers 方法来中断空闲的线程,interruptIdleWorkers 方法会使用 tryLock 方法来判断线程池中的线程是否是空闲状态

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        /**
         * This class will never be serialized, but we provide a
         * serialVersionUID to suppress a javac warning.
         */
        private static final long serialVersionUID = 6138294804551838833L;

        /** Thread this worker is running in.  Null if factory fails. */
        final Thread thread;  //真正执行task的线程
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;	//这就是需要执行的 task
        /** Per-thread task counter */
        volatile long completedTasks;   //完成的任务数,用于线程池统计

        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
        	//初始状态 -1,防止在调用 runWorker(),也就是真正执行 task前中断 thread。
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        /** Delegates main run loop to outer runWorker  */
        public void run() {
            runWorker(this);
        }

        // Lock methods
        //
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.

        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(); }

        void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
    }

runWorker方法

线程池在执行任务时,实际上是执行work的run方法。也就是调用ThreadpoolExecutor的runWorker方法。

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        //允许中断,因为创建Worker时将state设置成-1。而Worker的interruptIfStarted()方法只有state>=0才允许中断
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
           //task为空就从任务队列来拿到task
           //这里就会循环去任务队列里那任务,没有了就退出循环
            while (task != null || (task = getTask()) != null) {
           	 	//上锁,不是为了防止并发执行任务,为了在 shutdown()时不终止正在运行的 worker
                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 状态以上是要中断线程的
                //(Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP)确保线程中断标志位为 true 且是 stop 状态以上,接着清除了中断标志
                //!wt.isInterrupted()则再一次检查保证线程需要设置中断标志位
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();  //设置中断线程标识为true
                try {
                //任务执行前进行的操作默认没有任何操作,可以继承然后重写这个方法,加入自己的操作
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                    //执行任务的run方法,不用start是因为。start会开个新线程执行。
                        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 {
                    	//同beforeExecute一样
                        afterExecute(task, thrown);
                    }
                } finally {
                //置空任务(这样下次循环开始时,task 依然为 null,需要再通过 getTask()取) + 记录该 Worker 完成任务数量 + 解锁
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
        	//1.将入参 worker 从数组 workers 里删除掉;
			//2.根据布尔值 allowCoreThreadTimeOut 来决定是否补充新的 Worker 进数组workers
            processWorkerExit(w, completedAbruptly);
        }
    }

getTask方法

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

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

            // Check if queue empty only if necessary.
            //线程池状态为rs不为running并且workQueue为空或者为stop状态。workerCount-1
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null; //返回 null,则当前 worker 线程会退出
            }

            int wc = workerCountOf(c);
			// timed 变量用于判断是否需要进行超时控制。
			// allowCoreThreadTimeOut 默认是 false,也就是核心线程不允许进行超时;
			// wc > corePoolSize,表示当前线程池中的线程数量大于核心线程数量;
			// 对于超过核心线程数量的这些线程,需要进行超时控制
            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//1. 线程数量超过 maximumPoolSize 可能是线程池在运行时被调用了 setMaximumPoolSize()被改变了大小,否则已经 addWorker()成功不会超过 maximumPoolSize
//timed && timedOut 如果为 true,表示当前操作需要进行超时控制
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
//根据 timed 来判断,如果为 true,则通过阻塞队列 poll 方法进行超时控制,如果在
//keepaliveTime 时间内没有获取到任务,则返回 null.
//否则通过 take 方法阻塞式获取队列中的任务
            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)//如果拿到的任务不为空,则直接返回给 worker 进行处理
                    return r;
                timedOut = true;;//如果 r==null,说明已经超时了,设置 timedOut=true,在下次自旋的时候进行回收
            } catch (InterruptedException retry) {
                timedOut = false;// 如果获取任务时当前线程发生了中断,则设置 timedOut 为false 并返回循环重试
            }
        }
    }

processWorkerExit方法

private void processWorkerExit(Worker w, boolean completedAbruptly) {
		//如果worke出异常了,completedAbruptly的值还是true,需要手动减一
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
        	//计算线程池执行的所有任务个数
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }

        tryTerminate();

        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

tryTerminate方法

线程在结束前的操作

final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            if (workerCountOf(c) != 0) { // Eligible to terminate
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
            	//将线程池的状态改为TIDYING
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                    	//线程退出操作,方法为空,可以通过重载来实现自定义退出操作
                        terminated();
                    } finally {
                    	//将线程池的状态改为TERMINATED
                        ctl.set(ctlOf(TERMINATED, 0));
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

interruptIdleWorkers方法

interruptIdleWorkers方法表示中断可能正在等待任务的线程,忽略SecurityExceptions(在这种情况下,某些线程可能会保持不中断)。onlyOne如果为真,则最多打断一名工作人员。只有在以其他方式启用终止但仍有其他工作线程时,才从tryTerminate调用此函数。在这种情况下,如果所有线程当前都在等待,则最多会中断一个等待的工作线程来传播关闭信号。中断任意线程可以确保自关闭开始以来新到达的工人最终也会退出。为了保证最终的终止,只中断一个空闲的工作人员就足够了,但是shutdown()会中断所有空闲的工作人员,这样多余的工作人员就可以及时退出,而不是等待一个分散的任务完成

private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

关闭线程池

可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别,shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown方法来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow方法

合理配置线程池

要想合理地配置线程池,就必须首先分析任务特性,可以从以下几个角度来分析。
·任务的性质:CPU密集型任务、IO密集型任务和混合型任务。
·任务的优先级:高、中和低。
·任务的执行时间:长、中和短。
·任务的依赖性:是否依赖其他系统资源,如数据库连接。

性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务应配置尽可能小的线程,如配置Ncpu+1个线程的线程池。由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*Ncpu。混合型的任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量。如果这两个任务执行时间相差太大,则没必要进行分解。可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理。它可以让优先级高的任务先执行。
注意 如果一直有优先级高的任务提交到队列里,那么优先级低的任务可能永远不能执行。

执行时间不同的任务可以交给不同规模的线程池来处理,或者可以使用优先级队列,让执行时间短的任务先执行。依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,等待的时间越长,则CPU空闲时间就越长,那么线程数应该设置得越大,这样才能更好地利用CPU。

最好使用有界队列。因为如果线程执行任务时因为某些原因导致被阻塞,那么任务队列将会越来越长,如果是无界队列,有可能会撑爆内存,导致整个系统不可用。

创建线程池时,最好不用Executors去创建线程池,而是直接使用ThreadPoolExecutor的方式创建。这样对于各个参数,我们都可以控制。

线程池的监控

ThreadPoolExecutor类中提供了一些方法和属性,让我们可以对线程池进行监控,方便在出现问题时,可以根据线程池的使用状况快速定位问题。

·taskCount:线程池需要执行的任务数量。
·completedTaskCount:线程池在运行过程中已完成的任务数量,小于或等于taskCount。
·largestPoolSize:线程池里曾经创建过的最大线程数量。通过这个数据可以知道线程池是否曾经满过。如该数值等于线程池的最大大小,则表示线程池曾经满过。
·getPoolSize():线程池的线程数量。
·getActiveCount():获取活动的线程数。

还可以通过继承ThreadPoolExecutor类,来重写beforeExecute、afterExecute和terminated方法。通过阅读源码可以知道,这三个方法分别是线程在执行前,执行后,以及销毁时的方法。

线程池扩展实例

import java.util.Date;
import java.util.concurrent.*;

public class ThreadPoolCustomized extends ThreadPoolExecutor {

    public static  ConcurrentHashMap<String, Date> times = new ConcurrentHashMap<>();
    public ThreadPoolCustomized(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public ThreadPoolCustomized(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public ThreadPoolCustomized(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

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


    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        try{
            times.put(String.valueOf(r.hashCode()),new Date());
            System.out.println("线程:"+Thread.currentThread().getName()+"执行任务前!");
        } finally {
            super.beforeExecute(t,r);
        }
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        try {
            Date startTime = times.remove(String.valueOf(r.hashCode()));

            long time = new Date().getTime()-startTime.getTime();
            System.out.println("线程:"+Thread.currentThread().getName()+"执行任务后!");
            System.out.println("线程:"+Thread.currentThread().getName()+"执行任务耗费的时间:"+time+"ms");

        } finally {
            super.afterExecute(r,t);
        }
    }

    @Override
    protected void terminated() {
        try{
            System.out.println("线程:"+Thread.currentThread().getName()+"开始销毁线程池!");
        } finally {
            super.terminated();
        }
    }

    @Override
    public  void shutdown(){

        System.out.print("初始线程数:"+this.getPoolSize()+"\n");
        System.out.print("核心线程数:"+this.getCorePoolSize()+"\n");
        System.out.print("正在执行的任务数量:"+this.getActiveCount()+"\n");
        System.out.print("已经执行的任务数:"+this.getCompletedTaskCount()+"\n");
        System.out.print("任务总数:"+this.getTaskCount()+"\n");
        System.out.print("最大允许的线程数:"+this.getMaximumPoolSize()+"\n");
        System.out.print("线程允许的空闲时间:"+this.getKeepAliveTime(TimeUnit.MILLISECONDS)+"\n");
        System.out.println("已经执行的任务数"+this.getCompletedTaskCount()+
                "," +"正在执行的任务数:"+this.getActiveCount()+
                ",当前排队任务数:"+this.getQueue().size());

        super.shutdown();
    }
}
import java.util.concurrent.*;

public class TheradPoolDemo {
    public static ExecutorService pool = new ThreadPoolCustomized(3, 5, 10, TimeUnit.SECONDS,
            new ArrayBlockingQueue<Runnable>(2),
            Executors.defaultThreadFactory(),
            new RejectedExecutionHandler() {
                @Override
                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {

                    System.out.println("任务数已饱和,执行饱和策略!");
                }
            });
    public static void main(String[] args) {
        for (int i=0;i<10;i++){
            pool.execute(new Thread(()->{
                try {
                    Thread.currentThread().sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程"+Thread.currentThread().getName()+"启动任务!");
            }));
        }
        pool.shutdown();
    }
}

结果:

任务数已饱和,执行饱和策略!
任务数已饱和,执行饱和策略!
任务数已饱和,执行饱和策略!
线程:pool-1-thread-3执行任务前!
线程:pool-1-thread-1执行任务前!
线程:pool-1-thread-2执行任务前!
线程:pool-1-thread-5执行任务前!
线程:pool-1-thread-4执行任务前!
线程pool-1-thread-4启动任务!
线程:pool-1-thread-4执行任务后!
线程:pool-1-thread-4执行任务耗费的时间:1011ms
线程:pool-1-thread-4执行任务前!
线程pool-1-thread-5启动任务!
线程:pool-1-thread-5执行任务后!
线程:pool-1-thread-5执行任务耗费的时间:1011ms
线程:pool-1-thread-5执行任务前!
线程pool-1-thread-3启动任务!
线程:pool-1-thread-3执行任务后!
线程:pool-1-thread-3执行任务耗费的时间:1017ms
线程pool-1-thread-1启动任务!
线程pool-1-thread-2启动任务!
线程:pool-1-thread-2执行任务后!
线程:pool-1-thread-2执行任务耗费的时间:1018ms
线程:pool-1-thread-1执行任务后!
线程:pool-1-thread-1执行任务耗费的时间:1018ms
线程pool-1-thread-4启动任务!
线程:pool-1-thread-4执行任务后!
线程:pool-1-thread-4执行任务耗费的时间:1001ms
线程pool-1-thread-5启动任务!
线程:pool-1-thread-5执行任务后!
线程:pool-1-thread-5执行任务耗费的时间:1011ms
初始线程数:0
核心线程数:3
正在执行的任务数量:0
已经执行的任务数:7
任务总数:7
最大允许的线程数:5
线程允许的空闲时间:10000
已经执行的任务数7,正在执行的任务数:0,当前排队任务数:0
线程:pool-1-thread-5开始销毁线程池!

使用自己扩展的线程池时,可以自定义线程工厂以及饱和策略。线程工厂需要实现ThreadFactory接口,饱和策略需要实现RejectedExecutionHandler接口。从结果可以看出terminated()方法只执行了一遍。

Callable/Future 使用及原理分析

execute 和 submit 区别

线程池的执行方法有两个,execute 和 submit。他们的区别是:

  1. execute 只可以接收一个 Runnable 的参数,submit 可以接收 Runable 和 Callable 这两种类型的参数。
  2. execute 如果出现异常会抛出,submit 方法调用不会抛异常,除非调用 Future.get
  3. execute 没有返回值, 对于 submit 方法,如果传入一个 Callable,可以得到一个 Future 的返回值
public class CallableDemo implements Callable<String> {
	 @Override
 	public String call() throws Exception {
		 return "hello world";
	 }
	public static void main(String[] args) throws ExecutionException, InterruptedException {
 		CallableDemo callableDemo=new CallableDemo();
		FutureTask futureTask=new FutureTask(callableDemo);
 		new Thread(futureTask).start();
 		System.out.println(futureTask.get());
 	}
}

Callable/Future的用途是,当线程需要另一个线程的计算结果,所以传递一个回调接口给计算线程,当计算完成时,调用这个回调接口,回传结果值。Dubbo 的异步调用,比如消息中间件的异步通信等都用到这个

Callable/Future 原理分析

FutureTask

FutureTask的类图如下所示:

java 多线程 工厂模式 并发 java多线程并发编程 线程池_开发语言_03


FutureTask实现了RunnableFuture,而RunnableFuture又继承了Runnable和Future接口,Future 表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等。

public interface Future<V> {
 	boolean cancel(boolean mayInterruptIfRunning);
	 // 当前的 Future 是否被取消,返回 true 表示已取消
 	boolean isCancelled();
 	// 当前 Future 是否已结束。包括运行完成、抛出异常以及取消,都表示当前 Future 已结束
 	boolean isDone();
	 // 获取 Future 的结果值。如果当前 Future 还没有结束,那么当前线程就等待,
 	// 直到 Future 运行结束,那么会唤醒等待结果值的线程的。
	V get() throws InterruptedException, ExecutionException;
	// 获取 Future 的结果值。与 get()相比较多了允许设置超时时间
 	V get(long timeout, TimeUnit unit)
	 	throws InterruptedException, ExecutionException, TimeoutException;
}

state 的含义
表示 FutureTask 当前的状态,分为七种状态。

private static final int NEW = 0; // NEW 新建状态,表示这个 FutureTask还没有开始运行
// COMPLETING 完成状态, 表示 FutureTask 任务已经计算完毕了
// 但是还有一些后续操作,例如唤醒等待线程操作,还没有完成。
private static final int COMPLETING = 1;
// FutureTask 任务完结,正常完成,没有发生异常
private static final int NORMAL = 2;
// FutureTask 任务完结,因为发生异常。
private static final int EXCEPTIONAL = 3;
// FutureTask 任务完结,因为取消任务
private static final int CANCELLED = 4;
// FutureTask 任务完结,也是取消任务,不过发起了中断运行任务线程的中断请求
private static final int INTERRUPTING = 5;
// FutureTask 任务完结,也是取消任务,已经完成了中断运行任务线程的中断请求
private static final int INTERRUPTED = 6;

run方法

public void run() {
		// 如果状态 state 不是 NEW,或者设置 runner 值失败
		// 表示有别的线程在此之前调用 run 方法,并成功设置了 runner 值
 		// 保证了只有一个线程可以运行 try 代码块中的代码。
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {  //只有 c 不为 null 且状态 state 为 NEW 的情况
                V result;
                boolean ran;
                try {
                    result = c.call();   //调用 callable 的 call 方法,并获得返回结果
                    ran = true;  //运行成功
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);  //设置异常信息
                }
                if (ran)
                    set(result);  //设置结果
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

run方法作用非常简单,就是调用 callable 的 call 方法返回结果值 result,根据是否发生异常,调用 set(result)或 setException(ex)方法表示 FutureTask 任务完结。而且使用了CAS操作,保证并发安全。

get方法

public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

get 方法就是阻塞获取线程执行结果,这里主要做了两个事情:
1、判断当前的状态,如果状态小于等于 COMPLETING,表示 FutureTask 任务还没有完结,所以调用 awaitDone 方法,让当前线程等待。
2.、report 返回结果值或者抛出异常

awaitDone方法
表示如果结果还没有被执行完,把当前线程插入到等待队列

private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;  // 节点是否已添加
        for (;;) {
        	// 如果当前线程中断标志位是 true,
 			// 那么从列表中移除节点 q,并抛出 InterruptedException 异常
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state;
            if (s > COMPLETING) {  // 当状态大于 COMPLETING 时,表示 FutureTask 任务已结束。
                if (q != null)
                    q.thread = null;  // 将节点 q 线程设置为 null,因为线程没有阻塞等待
                return s;
            }
            // 表示还有一些后序操作没有完成,那么当前线程让出执行权
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            else if (q == null)
            	//表示状态是 NEW,那么就需要将当前线程阻塞等待。
 				// 就是将它插入等待线程链表中,
                q = new WaitNode();
            else if (!queued)
            // 使用 CAS 函数将新节点添加到链表中,如果添加失败,那么queued 为 false,
			 // 下次循环时,会继续添加,直到成功。
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            else if (timed) {  // timed 为 true 表示需要设置超时
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                //挂起当前线程nanos时间
                LockSupport.parkNanos(this, nanos);
            }
            else
            	//挂起当前线程
                LockSupport.park(this);
        }
    }

被挂起的线程在future执行run方法时,调用set方法时,会调用finishCompletion()方法。这里会执行unpark,将挂起的线程唤醒。

report方法

private V report(int s) throws ExecutionException {
        Object x = outcome;  //表示 call 的返回值
        if (s == NORMAL)  // 表示正常完结状态,所以返回结果值
            return (V)x;
        // 大于或等于 CANCELLED,都表示手动取消 FutureTask 任务,
		// 所以抛出 CancellationException 异常
        if (s >= CANCELLED)
            throw new CancellationException();
        // 否则就是运行过程中,发生了异常,这里就抛出这个异常
        throw new ExecutionException((Throwable)x);
    }

线程池对于 Future/Callable 的执行

AbstractExecutorService.submit

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

调用抽象类中的 submit 方法,这里其实相对于 execute 方法来说,只多做了一步操作,就是封装了一个 RunnableFuture