Java并发包中的线程池原理

1为什么使用线程池:

(1)当大量执行异步任务时线程池可以提供更好的性能,在不使用线程池时,每当需要执行任务时需要new一个线程来运行,而线程的创建和销毁都需要一定开销

(2)线程池提供了一种资源控制和管理的手段,比如控制线程个数,动态增加线程等。

2类图介绍:

Executors是一个工具类,里面提供了很多静态方法,我们可以根据不同的需求选取不同的线程池实例,具体如图所示:

下面我们来看看ThreadPoolExecutor的源码,主要了解一些关键的属性:

    //一个原子变量,用来记录线程池状态和线程池中线程的个数
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

    /*这里仔细解释,假设Integer类型是32位二进制表示,其中高三位表示线程池状态
      ,后面29位用来记录线程池中线程的个数*/
    private static final int COUNT_BITS = Integer.SIZE - 3;

    //表示线程的最大个数,往左移29位
    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;
    //所有任务都执行完后当前线程个数为0
    private static final int TIDYING    =  2 << COUNT_BITS;
    //线程池什么都不做
    private static final int TERMINATED =  3 << COUNT_BITS;

    //线程池是否处于Running状态
    private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }

   //同步阻塞队列
    private final BlockingQueue<Runnable> workQueue;

  
    private int largestPoolSize;

    private long completedTaskCount;
    //创建线程的工厂
    private volatile ThreadFactory threadFactory;

    //饱和策略,当队列满并且线程个数达到maximumPoolSize后采取的celve
    private volatile RejectedExecutionHandler handler;
    //线程池核心线程个数
    private volatile int corePoolSize;

   //线程池最大线程数量
    private volatile int maximumPoolSize;

    

线程池采用了一个很巧妙地设计,使用Integer原子变量来记录线程池状态和线程池中的线程个数,线程池中有一个内部类Worker,他继承了AQS,实现了不可重入独占锁:

private final class Workerextends AbstractQueuedSynchronizer implements Runnable{
       
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;

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

        

        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) {
                }
            }
        }
    }

下面我们来看看线程的执行方法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();
        }

        //如果线程池处于Running状态,则添加任务到阻塞队列
        if (isRunning(c) && workQueue.offer(command)) {
            //进行二次检查
            int recheck = ctl.get();
            //如果当前线程状态不是Running,则移除任务,并且执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //如果当前线程池为空,则添加一个线程
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //如果队列满,则新增线程,如果增加失败则执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }

这段代码我在注释里面解释的比较详细了,这里解释一下为什么要进行二次校检,这是因为添加新任务后,线程池状态可能已经发生了变化,所有有必要进行二次检查,下面我们来看看addWorker()方法:

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

            //检查是否满足条件,这个地方我有点没看懂,但有一个条件是如果线程池状态为SHUTDOWN并            
            //且任务队列为空则返回false
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            //CAS自旋增加线程个数
            for (;;) {
                //获取线程的个数
                int wc = workerCountOf(c);
                //如果线程个数超过容量则返回false
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                采用CAS增加线程个数
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                重新读取线程状态和线程个数
                c = ctl.get();  
                //查看线程池状态是否变
                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 {
            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());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) 
                        //添加任务线程
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                //添加成功后启动任务
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

这段代码主要进行了两部分工作:第一通过循环CAS操作增加线程的数目,第二通过加锁将人物添加到workers队列中,并且启动任务执行,addWorler()方法主要完成这两部分工作;到这里线程池的大体执行过程已经讲解完成了,当然这个类里面还有许多其它的细节,我也只能讲出自己理解的一部分,以后有时间继续学习!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值