线程池---核心方法总结

ThreadPoolExecutor中的关键定义:

  • Worker:内部类,继承了Runnable和AQS,定义了Thread线程对象。
  • ctl:AtomicInteger类型对象,有两层含义:workerCount,表明当前有效的线程数;runState,表明当前线程池的状态,是否处于Running,Shutdown,Stop,Tidying,Terminate五种状态。
  • workQueue:BlockingQueue<Runnable>类型,线程池的队列。
  • mainLock:ReentrantLock类型,线程池的锁。
  • workers:HashSet<Worker>类型,存放正在被执行的任务和线程。

一、主流程方法execute

        使用executor.execute(worker) 来提交一个任务到线程池中去,这个方法非常重要,参数中什么时候创建核心线程数,什么时候创建最大线程数,就是在这里体现,下面我们来看看它的源码:

        addWorker(null,false)的解释:

当任务添加到线程池阻塞队列成功后,重新检查一下现在线程池中是否有活跃线程。有可能存在这种情况,用户设置的核心线程数是0,在第1处时正好有1个线程在执行任务,所以第一处的if条件不成立,此时当前执行execute方法的线程被CPU调度为等待状态,当再次被调度执行时,刚刚线程池中的那个线程已经超过销毁时间而被销毁了。所以此时线程池中的线程数为0,如果不做recheck的话,那么这个任务有可能永远得不到执行。所以这是作者充分考虑了可能存在的异常情况,当新任务添加到队列后,重新检查一下线程池,如果没有活跃线程了,就执行addWorker方法

二、Worker类解析

        线程池中的线程,都会被封装成一个Worker类对象,ThreadPoolExecutor维护的其实就是一组Worker对象,其中用集合workers存储这些Worker对象;

        Worker类中有两个属性,一个是firstTask,用来保存传入线程池中的任务,一个是thread,是在构造Worker对象的时候,利用ThreadFactory来创建的线程,用来处理任务的线程;

        Worker继承AQS,使用AQS实现独占锁,并且是不可重入的,构造Worker对象的时候,会把锁资源状态设置成-1,因为新增的线程,还没有处理过任务,是不允许被中断的。

        也就是说Worker对象本身就有lock()、unlock()、tryLock()、isLocked()等方法可以调用,用来给Worker对象加锁。

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable{
    ....
    
    Worker(Runnable firstTask) {
            setState(-1); //AQS的方法,初始化线程状态
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);//工程创建线程
    }

    protected boolean isHeldExclusively() {
        return getState() != 0;
    }

     public void run() { //线程池的执行方法。
            runWorker(this);
     }    

    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) { //AQS的方法,CAS设置状态
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }
    
    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }
    
    public void lock()        { acquire(1); //AQS的方法,加锁 }
    public boolean tryLock()  { return tryAcquire(1); }
    public void unlock()      { release(1); //AQS的方法,释放锁  }
    public boolean isLocked() { return isHeldExclusively(); } 

 void interruptIfStarted() { 
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();  //友好终止线程
                } catch (SecurityException ignore) {
                }
            }
        }
    ....
}

lock 方法一旦获取了独占锁,表示当前线程正在执行任务中;那么它会有以下几个作用

  • 如果正在执行任务,则不应该中断线程;
  • 之所以设置为不可重入,是因为我们不希望任务在调用像 setCorePoolSize 这样的线程池控制方法时重新获取锁,这样会中断正在运行的线程
  • 线程池在执行 shutdown 方法或 tryTerminate 方法时会调用 interruptIdleWorkers 方法来中断空闲的线程,interruptIdleWorkers 方法会使用 tryLock 方法来判断线程池中的线程是否是空闲状态
  • 如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断;

三、addWorker方法,创建线程的方法

        addWorker这个方法主要用来创建新的工作线程,如果返回true说明创建和启动工作线程成功,否则的话返回的就是false。

参数:code为true时,表是创核心线程执行任务;为flase时,创建最大线程执行。

四、runWorker(Worker w)核心方法

        runWorker是ThreadPoolExecutor的方法被内部类Worker中的run方法调用,线程执行任务的核心方法,创建对象时会被隐式执行。

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        //释放锁,将state设置为0,允许中断任务的执行
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
        //如果任务不为空,或者从任务队列中获取的任务不为空,则执行while循环
            while (task != null || (task = getTask()) != null) {
            //如果任务不为空,则获取Worker工作线程的独占锁
                w.lock();
                //如果线程已经停止,或者中断线程后线程终止并且没有成功中断线程
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    //中断线程
                    wt.interrupt();
                try {
                //执行任务前执行的逻辑
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                    //调用Runable接口的run方法执行任务
                        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;
                    //完成的任务数量加1
                    w.completedTasks++;
                    //释放工作线程获得的锁
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
        //执行退出Worker线程的逻辑
            processWorkerExit(w, completedAbruptly);
        }
    }

五、getTask() 获取任务

getTask()被runWorker调用,是工作线程在while循环中获取任务队列中的任务对象的方法;

什么情况下getTask()返回null?

1.rs >= SHUTDOWN 成立: 当前的状态最低也是STOP状态,一定要返回 null了。

2.前置条件 状态是 SHUTDOWN, workQueue.isEmpty()

3.线程池中的线程数量,超过最大限制时,会有一部分线程返回null

4.线程池中的线程数超过 corePoolSize 时,会有一部分线程可能返回null

   private Runnable getTask() {
        // 表示当前线程获取任务是否超时; 默认 false, 未超时
        boolean timedOut = false; // Did the last poll() time out?

        // 自旋
        for (;;) {
            // 获取最新ctl值保存到c中
            int c = ctl.get();
            // 获取线程池当前运行状态
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            // 条件1: 条件成立:说明当前线程池是非RUNNING状态。
            // 条件2: 2.1成立说明当前状态最低是STOP状态;
            // 2.2 说明队列是null
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                // 使用CAS +  死循环的方式让ctl的值 -1
                decrementWorkerCount();
                return null;
            }

            // 执行到这里,有几种情况?
            // 1.线程池是RUNNING状态
            // 2.线程池是SHUTDOWN状态 但是队列还未空,此时可以创建线程

            // 获取线程池中的线程数量
            int wc = workerCountOf(c);

            // Are workers subject to culling?
            // true: 表示当前这个线程 获取 task 时 是支持超时机制的,
            // false: 表示当前线程不支持超时机制。 核心线程数量内的线程会使用 queue.take();

            // 情况1: allowCoreThreadTimeOut == ture 表示核心线程数量内的线程,可以被回收; false不可以被回收
            // wc > corePoolSize :  当前线程池中的数量时大于核心线程数的。
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            // 条件1:(wc > maximumPoolSize || (timed && timedOut): 为ture 不代表线程一定返回null
            // 1.1 wc > maximumPoolSize : 可能外部线程将线程池最大线程数设置为比初始化时的要小
            //1.2  timed && timedOut :当前线程使用poll方式获取task。 上一次循环时, 使用 pool方式获取任务时,超时了。

            // 条件二:   (wc > 1 || workQueue.isEmpty())
            // 2.1: wc > 1: 说明当前线程池中,还有其他线程;当前线程可以直接回收,返回null。
            // 2.2: 说明当前任务队列已经为null。 最后一个线程也可以放心退出
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                // 使用CAS方式进行退出当前线程,计数 -1;
                if (compareAndDecrementWorkerCount(c))
                    return null;

                //CAS失败,就继续;再次自旋时,timed可能为 false
                continue;
            }

            try {

                // 获取任务的逻辑
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();

                // 条件成立: 返回任务
                if (r != null)
                    return r;

                // 说明当前线程超时了。
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

六、processWorkerExit()方法来销毁工作线程

        在runWorker(Worker w)方法finally中被调用,来销毁工作线程。该方法就是判断当前线程是需要将其删除还是继续执行任务。该方法也是ThreadPoolExecutor中的方法。

        但这个方法只有在一定情况下才会起作用,如果已经从队列中取不出任务来了,或者在worker执行任务过程中出现了错误,这个方法就起到了作用,如果正常的话这个方法就没啥用。


    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            //异常退出,worker数量减一
            //这个worker是正常工作的,但是现在没有任务了,要退出了,数量就不减1了?
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            //汇总该worker(一个worker实际上就是一个线程)完成的任务数量到线程池
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }
        
        //退出woker的时候,检查下线程池状态
        tryTerminate();

        int c = ctl.get();
        //线程池的状态是runing 或者 shutdown
        if (runStateLessThan(c, STOP)) {
            //是因为没有任务了才要终止这个线程的
            if (!completedAbruptly) {
                //假如allowCoreThreadTimeOut是false,那么线程池中的线程数量至少是corePoolSize
                //假如allowCoreThreadTimeOut是true,那么一旦没有任务了,所有线程就都终止了
              //这里有一个问题,因为正常退出的话,worker数量没有减1,所以即使worker数量满足要求,但worker线程却没有了,剩下的任务就没法执行了
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                //目前正在工作的线程数量大于等于min,则正常结束这个线程(就是维持线程数量至少是min)
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            //增加一个线程
            addWorker(null, false);
        }
    }

七、interruptIdleWorkers()中断空闲线程

        interruptIdleWorkers()方法的调用不是在本工作线程,而是在主线程中调用的。被shutdown()和shutdownNow(没有tryLock,直接暴力中断线程),tryTerminate()等调用。

    //尝试关闭空闲的线程。
    private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock(); // 主线程加锁。
        try {

            //循环遍历workers,尝试获取锁tryLock,如果成功,执行中断操作interrupt
            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();
        }
    }

抛出问题:

怎么添加到队列里面的?

从队列里面获取任务后怎么选择线程执行的?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值