如何实现一个线程池

线程池底层原理

ThreadPoolExecutor解读

1、线程池的状态

Running:运行状态,可以接收异步任务

ShutDown:不接收异步任务,但还会继续执行阻塞队列中的已提交的任务

Stop:不再接收异步任务,也不会执行阻塞队列中已提交的任务,而且也会中断线程池中正在运行的任务

Tidying:所有任务都被终止了,此时CountWork数为0,为此状态时还将调用terminated()方法

Terminated:terminated()方法调用完成后变成此状态

在我们的ThreadPoolExecutor中有一个变量ctl

 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

其前3位代表了当前线程池的状态,后29位代表线程池中工作线程的数量

从而衍生了判断线程池以及获取工作线程数的方法,就是通过ctl前3位来设置和获取

    // ctl(高3位保存运行时状态)  低29位保存有效的线程数
	private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
	// 值为29
	private static final int COUNT_BITS = Integer.SIZE - 3;
	// workerCount的上限最大值
	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;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
	//判断线程池是否在运行
	private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }

2、关于execute,任务提交的执行流程

相信很多线程池的八股文都涉及到

1、首先判断工作线程数是否达到核心线程数,如果没达到则将任务直接addWorker(task,true)

addWorker(Runnable task,boolean core), core为true代表是由核心线程进行执行,false为临时线程执行

2、如果达到核心线程数,则要准备往阻塞队列进行添加,此时需要判断阻塞队列是否满,如果不满,则添加到阻塞队列

3、如果阻塞队列满了,则执行拒绝策略

而ThreadPoolExecutor的execute可能还需要要考虑其他的因素

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException(); //为空
    
        int c = ctl.get(); //获取大名鼎鼎的ctl
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))  //********************#1
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command)) //************2
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

源码解析:参自Java线程池的底层实现与使用 - StoneGeek - 博客园 (cnblogs.com)

1、addWorker失败的可能原因

1、线程池已经被ShutDown,不在接收新的任务

2、并发场景下,其他线程抢先创建了worker线程导致workCount>=corePoolSize

2、recheck双重校验的重用

再次获取ctl的作用,主要是判断刚加入阻塞队列后:

1、如果线程池不处于Running状态,则拒绝该任务加入阻塞队列,(将其从阻塞队列中踢出)

2、else if (workerCountOf(recheck) == 0),如果线程池是运行状态,或者从workQueue中删除任务失败(刚好有一个线程执行完毕,并消耗了这个任务),确保还有线程执行任务(只要有一个就够了)

3、关于addWorker(Runnable task,boolean core)

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            //获取线程池的状态
            int rs = runStateOf(c);
            // Check if queue empty only if necessary.
            //判断是否被shundown
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            //
            for (;;) {
                int wc = workerCountOf(c);  //获取工作线程数
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;  //
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                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 {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        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;
}

源码解析:

1、判断线程池当前是否为可以添加worker线程的状态,可以则继续下一步,不可以return false:
A、线程池状态>shutdown,可能为stop、tidying、terminated,不能添加worker线程
B、线程池状态shutdown,firstTask不为空,不能添加worker线程,因为shutdown状态的线程池不接收新任务
C、线程池状态
shutdown,firstTask==null,workQueue为空,不能添加worker线程,因为firstTask为空是为了添加一个没有任务的线程再从workQueue获取task,而workQueue为空,说明添加无任务线程已经没有意义

2、线程池当前线程数量是否超过上限(corePoolSize 或 maximumPoolSize),超过了return false,没超过则对workerCount+1,继续下一步

3、在线程池的ReentrantLock保证下,向Workers Set中添加新创建的worker实例,添加完成后解锁,并启动worker线程,如果这一切都成功了,return true,如果添加worker入Set失败或启动失败,调用addWorkerFailed()逻辑

    private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w);
            decrementWorkerCount();
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }

4、关于内部类Worker

Worker及实现了Runnable又继承了AQS

实现Runnable可以理解,继承AQS是干什么呢? 答:实现锁的效果

        Worker(Runnable firstTask) {
            setState(-1); //将AQS的state置为-1,在runWoker()前不允许中断,0为未锁定状态,>0为重入次数
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

Worker可以控制中断主要有以下几方面的原因

1、AQS初始状态为-1,此时不允许中断interrupt(),只有在worker线程启动了,执行了runWork(),才能中断

不允许中断体现在:
A、shutdown()线程池时,会对每个worker tryLock()上锁,而Worker类这个AQS的tryAcquire()方法是固定将state从0->1,故初始状态state==-1时tryLock()失败,没发interrupt()
B、shutdownNow()线程池时,不用tryLock()上锁,但调用worker.interruptIfStarted()终止worker,interruptIfStarted()也有state>0才能interrupt的逻辑
2、为了防止某种情况下,在运行中的worker被中断,runWorker()每次运行任务时都会lock()上锁,而shutdown()这类可能会终止worker的操作需要先获取worker的锁,这样就防止了中断正在运行的线程

Worker实现的AQS为不可重入锁,为了是在获得worker锁的情况下再进入其它一些需要加锁的方法

    private final class Worker extends AbstractQueuedSynchronizer implements Runnable
    {
        /** 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;
		
        
        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) {
                }
            }
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值