线程池ThreadPoolExecutor使用和原理

概述

线程提高了程序的运行效率,但线程的创建需要占用内存空间,大量的线程必然会加重系统的负担从而影响性能。而线程池的出现则避免了线程的重复创建,总结来说线程池有三个优势:1.复用已创建的线程,降低资源消耗。2.控制线程池的最大并发数,防止资源抢夺。3.有效管理线程池内的线程,提高稳定性。

ThreadPoolExecutor

Executor是一个任务执行接口,ExecutorService继承了Executor,并做了一些扩展,是真正的线程池接口,AbstractExecutorService这个抽象类实现了这个接口的大部分方法,而我们的ThreadPoolExecutor则是继承了AbstractExecutorService,是真正的线程池实现。我们来看他的一个构造方法。

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

参数介绍:
corePoolSize
线程池内的核心线程数,即允许并发的最大线程数。
maximumPoolSize
线程池内允许存在的最大线程数。超过这个数量的线程任务将会被拒绝。
keepAliveTime
闲置线程的存活时间,默认情况下,只有在线程数大于corePoolSize时,才会创建非核心线程,如果非核心线程闲置时间超过keepAliveTime就会被回收,allowCoreThreadTimeOut为true时,同样适用于核心线程。
unit
keepAliveTime的单位。
workQueue
线程池中的任务队列,当线程池中的线程数超过corePoolSize,线程会被放入workQueue进行等待。
threadFactory
线程工厂,用来创建新线程。
handler
当线程池已满活无法成功执行任务的拒绝策略。

ThreadPoolExecutor执行策略

1.如果线程池中的线程数量小于corePoolSize,则会直接创建一个核心线程执行任务。
2.如果线程池中的线程数量大于等于corePoolSize,并且任务队列未满,则会将任务放到任务队列中等待执行。
3.如果线程池中的线程数量大于等于corePoolSize,但任务队列已满,并且线程总数还未超过允许的最大线程数maximumPoolSize,则会创建一个非核心线程执行任务。
4.如果线程池中的线程数量大于等于corePoolSize,但任务队列已满,并且线程总数达到允许的最大线程数maximumPoolSize,则会直接拒绝执行此任务。

ThreadPoolExecutor的任务缓存队列

任务缓存队列(workQueue)的作用是当线程池内的核心线程数达到最大核心线程数后,缓存继续到来的新的任务,它是一个BlockingQueue类型,主要由其三个子类执行。
1.ArrayBlockingQueue
基于数组的队列,和数组一样,创建时需要指定大小。
2.LinkedBlockingQueue
基于链表的队列,比较灵活,可以指定大小,也可以不指定,默认大小为Integer.MAX_VALUE。
3.synchronousQueue
这个队列比较特殊,它无法存储元素,但会直接创建一个线程来执行新任务。

ThreadPoolExecutor源码

话不多说,我们直接来看execute()方法

public void execute(Runnable command) {
		//任务为空,抛出异常
        if (command == null)
            throw new NullPointerException();
       	//获取原子数 
        int c = ctl.get();
        //核心线程数<corePoolSize,则创建新的核心线程
        if (workerCountOf(c) < corePoolSize) {
        	//创建新的核心线程
            if (addWorker(command, true))
            	//创建成功,返回
                return;
            //创建失败,重新获取原子数(应该是防止并发时有新的线程创建从而原子数改变)
            c = ctl.get();
        }
        //此时是创建新核心线程失败的情况下
        //线程池存活的情况下将任务插入到任务队列
        if (isRunning(c) && workQueue.offer(command)) {
        	//插入成功
        	//再次校验
            int recheck = ctl.get();
            //如果线程池挂掉了,就再把刚插入的任务remove掉
            if (! isRunning(recheck) && remove(command))
            	//之后拒绝执行任务
                reject(command);
             //如果线程池中一个线程都没有
            else if (workerCountOf(recheck) == 0)
            	//创建一个无任务的线程,并且不占用corePoolSize,
            	//为什么不让线程直接执行command任务呢,
            	//因为上面已经是把command添加到任务队列了,
            	//只需要创建一个线程,它会自动执行之前workQueue.offer(command)添加的任务。
                addWorker(null, false);
        }
        //插入失败(可能队列已满)就新建线程执行任务
        else if (!addWorker(command, false))
        	//新建失败(超过最大线程数),拒绝执行任务
            reject(command);
    }

具体逻辑我已经在注释中标明了,思路还是比较清晰的。下面我们继续看addWorker()方法。

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            //获取运行状态
            //RUNNING    = -1
			//SHUTDOWN   =  0
			//STOP       =  1
			//TIDYING    =  2
			//TERMINATED =  3
            int rs = runStateOf(c);

            // 1.满足rs不是RUNNING状态
            // 2.rs不为SHUTDOWN,任务firstTask不为空,workerQueue为空
            // 返回false,创建线程失败
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                //如果已达到最大核心线程数或最大线程数,返回false,不新建线程
                //这里的core为true时是创建核心线程,false是创建非核心线程
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //worker数+1
                if (compareAndIncrementWorkerCount(c))
                	//成功 跳出全部循环
                    break retry;
                c = ctl.get();  // Re-read ctl
                //失败 判断线程池状态有无改变
                if (runStateOf(c) != rs)
                	//状态改变跳出内部循环,继续最外部循环
                    continue retry;
                    //没有改变 继续尝试worker数+1
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
		/**
     	* 走到这里,说明worker数已成功+1
     	* 接下来开始真正的创建线程
     	* Worker类内部维护了一个Thread和一个Runnable
     	*/
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
        	// 创建worker 传入task,并通知ThreadFactory创建一个线程
            w = new Worker(firstTask);
            // 获取worker的线程
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                // 上锁
                //------------------------------------------------
                mainLock.lock();
                try {
                    // 再次检查状态,
                    // 1.线程池正在运行 
                    // 2.线程池虽被shutdown,但并没有任务;
                    int rs = runStateOf(ctl.get());
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        //如果线程已经启动了,就抛出异常
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        //把worker存储到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 {
        	// 线程开启失败,即worker没有添加成功,就执行addWorkerFailed
            if (! workerStarted)
            	// worker数-1
                addWorkerFailed(w);
        }
        return workerStarted;
    }

到这里可能没找到task的执行,实际上它是在Worker这个类中关联的。

	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;
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        /** 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) {
            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) {
                }
            }
        }
    }

实际上Worker类也是个任务类,实现了Runnable,并且继承了AbstractQueuedSynchronizer,所以它既可以执行任务,又有锁的机制,可以看到在Worker的构造方法中用ThreadFactory创建了线程thread,并关联了当前worker,而其run()方法是任务的真正实现,内部调用了ThreadPoolExecutor的runWorker(this)方法,来看一下具体实现。

final void runWorker(Worker w) {
		//获取当前线程
        Thread wt = Thread.currentThread();
        //获取需要执行的任务
        Runnable task = w.firstTask;
        //释放worker中的任务
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            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);
        }
    }

到这里我们看到了execute的整个执行流程,对线程池也有了大致的了解,接下来我们就看一下常用的四种已定义好的线程池。

FixedThreadPool

通过Executors的newFixedThreadPool创建,它的特点是拥有固定的线程数且只有核心线程,并且没有超时机制。
当有任务到来时,会创建核心线程来执行任务,如果线程数已满,则将任务放入任务队列,等有空闲线程时再取出任务执行,由于队列是LinkedBlockingQueue,所以任务队列也没有限制,即使所有线程都空闲出来也不会回收,依然占据资源,除非主动shutdown。

CachedThreadPool

通过Executors的newCachedThreadPool创建,他是一个只有非核心线程的线程池,最大线程数为Integer.MAX_VALUE,一个无限大的数,所以这个线程池内的线程可以无限大,当有任务来时,如果池内没有空闲的线程,就会创建新线程来处理任务,空闲线程的超时时间为60s,所以这个线程池适合处理大量耗时少的任务,它的阻塞队列是,SynchronousQueue。

ScheduledThreadPool

通过Executors的newScheduledThreadPool创建,它的核心线程是固定的,但非核心线程没有限制,并且非核心线程一旦空闲就会被回收,通常用于执行周期性任务。

SingleThreadExecutor

通过Executors的newSingleThreadExecutor创建,它只有一个核心线程,有较多的任务时,会先放入阻塞队列,保证每次执行一个任务,遵循先进先出的原则。

总结

使用线程池可以自定义ThreadPoolExecutor,也可以使用常用的四种方式,线程池的知识点还有很多,以后会逐步完善自己的知识库。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值