Java线程池ThreadPoolExecutor详解

线程池出现的原因

为什么需要线程池呢?我们直接通过new Thread创建线程不就好了吗?
Thread对象和普通的对象不一样,普通的对象在JVM中划分一块内存区域将数据存到里面就行了,而Thread需要调用操作系统的API来为Thread分配资源,需要从用户态切换到内核态,十分消耗资源,所以Thead是一个重量级对象,要避免线程频繁的创建和销毁,所以就引入了线程池!

线程池实例

//任务队列
BlockingQueue queue = new LinkedBlockingQueue(10);

ThreadPoolExecutor executor = new ThreadPoolExecutor(4,10,2,TimeUnit.SECONDS,queue);

executor.execute(new Runnable() {
    @Override
    public void run() {
        System.out.println("test");
    }
});

源码解析

主要属性

//线程池的运行信息标志,前3位用来表示线程池的运行状态,后29位用来记录当前线程的个数
//初始值为111 0 0000 0000 0000 0000 0000 0000 0000,代表运行状态,线程个数为0
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

//用来记录线程个数的位数  32 - 3 = 29
private static final int COUNT_BITS = Integer.SIZE - 3;
//容纳线程的最大容量,000 1 1111 1111 1111 1111 1111 1111 1111
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

//线程池的五个状态,RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED值依次增大
//running  111 0 0000 0000 0000 0000 0000 0000 0000
private static final int RUNNING    = -1 << COUNT_BITS;
//关闭  000 0 0000 0000 0000 0000 0000 0000 0000
private static final int SHUTDOWN   =  0 << COUNT_BITS;
//停止  001 0 0000 0000 0000 0000 0000 0000 0000
private static final int STOP       =  1 << COUNT_BITS;
//      010 0 0000 0000 0000 0000 0000 0000 0000
private static final int TIDYING    =  2 << COUNT_BITS;
//      011 0 0000 0000 0000 0000 0000 0000 0000
private static final int TERMINATED =  3 << COUNT_BITS;

//用来获取线程运行状态 c & 111 0 0000 0000 0000 0000 0000 0000 0000
private static int runStateOf(int c)     { return c & ~CAPACITY; }
//用来获取线程个数   c & 000 1 1111 1111 1111 1111 1111 1111 1111
private static int workerCountOf(int c)  { return c & CAPACITY; }
//用来初始化ctl
private static int ctlOf(int rs, int wc) { return rs | wc; }
//工作线程集合
private final HashSet<Worker> workers = new HashSet<Worker>();

流程

初始化

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

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.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

1、corePoolSize:核心线程个数,
2、maximumPoolSize:最大线程个数
3、workQueue:待执行的任务队列,我们最好使用有界队列,如果设置无界队列,很容易导致OOM。
4、keepAliveTime:线程的最大空闲时间,如果超过keepAliveTime个TimeUnit时间内没有执行任务,非核心线程就会被回收。如果想要回收核心线程的话,需要设置allowCoreThreadTimeOut(true)。
5、TimeUnit:keepAliveTime的时间单位
6、threadFactory:创建线程的工厂
7、hanlder:当线程池不够用的时候的拒绝策略,有四种策略,后面我们结合源码详细讲。

线程池中的线程被分为两类:
1、核心线程:一般不会被回收
2、非核心线程:如果连续keepAliveiTime个TimeUnit时间内没有执行任务,就会被回收。

2、execute()

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();

    int c = ctl.get();//获取当前线程池信息
    //workerCountOf用来获取线程个数,如果线程个数小于corePoolSize,就增加线程
    //线程池中的线程是懒加载的,当用到的时候才会加载,初始化的时候不会加载。
    if (workerCountOf(c) < corePoolSize) {
        //添加线程,command代表任务,true代表是否是核心线程
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    
    //如果当前线程数量>=corePoolSize,并且线程池处于运行状态就将任务加入wordQueue中
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        //再次检测线程池状态,如果线程池不运行了,就将command从wordQueue中移除
        if (! isRunning(recheck) && remove(command))
            //返回拒绝策略
            reject(command);
        //如果当前工作线程数量为0,才会加入一个非核心线程
        else if (workerCountOf(recheck) == 0)
            //加入非核心线程
            addWorker(null, false);
    }
    //如果加入workQueue失败(队列满了),才会加入一个非核心线程,如果加入非核心线程失败,就执行拒绝策略
    else if (!addWorker(command, false))
        reject(command);
}

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        //获取线程运行状态
        int rs = runStateOf(c);
        //如果线程不是运行状态 或者 firstTask== null 或者 workQueue为空,返回false
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        for (;;) {
            //获取线程个数
            int wc = workerCountOf(c);
            //1、如果wc大于最大的线程个数CAPACITY,返回false
            //2、如果是核心线程,大于等于corePoolSize,返回false
            //3、如果是非核心线程,大于等于maximumPoolSize,返回false
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;

           //将c++,也就是线程个数+1
            if (compareAndIncrementWorkerCount(c))
                //退出循环
                break retry;
            //获取c
            c = ctl.get();  // Re-read ctl
            //如果线程池运行状态发生变化,重新循环
            if (runStateOf(c) != rs)
                continue retry;
            
        }
    }

    //线程是否开始工作
    boolean workerStarted = false;
    //线程是否加入到queue中
    boolean workerAdded = false;
    //线程
    Worker w = null;
    try {
        //初始化Worker线程
        w = new Worker(firstTask);
        //Worker(Runnable firstTask) {
        //    setState(-1);//设置AQS的state,Worker是继承AQS的。
        //    this.firstTask = firstTask;//Thread的第一个任务,优先执行
        //    worker的线程为getThreadFactory的新建线程
        //    this.thread = getThreadFactory().newThread(this);
        //}
        final Thread t = w.thread;
        if (t != null) {
            //mainLock是ThreadPoolExecutor中的一个属性,Worker是一个内部类
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                //获取线程状态
                int rs = runStateOf(ctl.get());
                //线程池处于运行状态,或者线程池处于关闭状态,并且firstTask = null
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    //如果线程还活着,抛出异常
                    if (t.isAlive())
                        throw new IllegalThreadStateException();
                    //工作线程集合中加入w
                    workers.add(w);
                    int s = workers.size();
                    //更新largestPoolSize的值
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    //加入成功
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                //启动worker
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        //加入异常
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

通过上述代码我们看出execute的流程
1、如果线程个数小于corePoolSize,就加入一个核心线程Worker,并将其command设置为worker的firstTask,优先执行。
2、如果线程个数大于等于corePoolSize,如果线程池处于运行状态,就优先将command加入到workerQueue中。
如果加入workerQueue中失败,这时很有可能是workerQueue队列满了,这时会尝试加入一个非核心线程worker,将worker的firstTaks设置为command。
3、在加入worker的时候,如果线程不处于运行状态,或者workerQueue为空,或者如果加入核心线程,线程个数大于corePoolSize,如果是非核心线程,线程个数大于maximumPoolSize都会导致加入失败。

接下来我们说下Worker的run方法

public void run() {
    runWorker(this);
}
final void runWorker(Worker w) {
//获取w的Thread
Thread wt = Thread.currentThread()
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); 
boolean completedAbruptly = true;

try {
    //从这里可以看出优先执行firstTask
    //getTask()是从workQueue中获取任务
    while (task != null || (task = getTask()) != null) {
        w.lock();
        //如果线程状态为STOP、TIDYING、TERMINATE或者线程被中断的话,就线程自我中断
        if ((runStateAtLeast(ctl.get(), STOP) ||
             (Thread.interrupted() &&
              runStateAtLeast(ctl.get(), STOP))) &&
            !wt.isInterrupted())
            wt.interrupt();
            
        try {
        	//protected void beforeExecute(Thread t, Runnable r) { }
            //在任务执行前调用的方法,用户可以继承ThreadPoolExecutor自定义实现,比如日志什么的
            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 {
                //任务执行之后调用的方法,用户也是可以继承ThreadPoolExecutor自定义实现的,比如日志什么的
                afterExecute(task, thrown);
            }
        } finally {
            task = null;
            //worker完成的任务数++;
            w.completedTasks++;
            w.unlock();
        }
    }
    completedAbruptly = false;
} finally {
    processWorkerExit(w, completedAbruptly);
}
}

//从workerQueue中获取任务
private Runnable getTask() {
    boolean timedOut = false; // 获取task是否超时

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

        //如果线程处于SHUTDOWN、STOP、TERMINATE、TIDYING或者workQueue为空,
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();//将workcount--
            //返回了null,worker的run方法也会调用结束,对应的worker也会停止
            return null;
        }
        //工作线程个数
        int wc = workerCountOf(c);

        //allowCoreThreadTimeOut为true代表着核心线程如果长时间不执行任务,也会被终止
        //线程是否会被回收,如果设置了allowCoreThreadTimeOut或者线程个数大于corePoolSize
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        //如果线程个数大于maximumPoolSize,或者线程个数大于1,但是workQueue为空,就会减少线程的个数
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            //worker的个数--
            if (compareAndDecrementWorkerCount(c))
                return null;返回了null,worker的run方法也会调用结束,对应的worker也会停止
            continue;
        }

        try {
            //如果设置了可回收,就以keepAliveTime为超时时间申请task
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            //如果超时了或者workQueue为空,timedOut为true,在下一轮循环中会如果timed = true的话会返回null,将线程回收
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

通过以上代码,我们可以看出Worker的工作流程
1、worker设置了一个while循环,循环的条件是firstTaks!=null,或者能够从workQueue中获取到task
2、如果firstTask不为null,就会优先执行firstTask。
3、如果firstTask为null,就会到workQueue中获取task
4、如果获取不到workQueue,也就是task == null,线程worker就会停止,在返回null之前会利用CAS将线程个数-1.

以下情况会返回null
1、如果允许回收核心线程allowCoreThreadTimeOut(true)并且核心线程获取task超时,并且workQueue为空
2、如果线程个数>maxPoolSize
3、如果线程池状态为STOP、TERMINATE、TIDYING或者线程池状态为SHUTDOWN并且workQueue为空。

拒绝策略
当调用execute()方法失败的时候,就会发出拒绝策略。
拒绝策略的接口为RejectedExecutionHandler,其有四个子类,对应着四种不同的处理方法,在初始化的时候,我们可以指定四个中的任意一个。
1、DiscardOldestPolicy

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        e.getQueue().poll();
        e.execute(r);
    }
}

从上述代码看出,DiscardOldestPolicy会将最老的任务从workQueue中出队,然后继续执行当前的command

2、DiscardPolicy

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
 }

DiscardPolicy就是将这个任务直接丢弃,什么都不干

3、AbortPolicy

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task " + r.toString() +
                                         " rejected from " +
                                         e.toString());
}

AbortPolicy的做法是抛出异常。

4、CallerRunsPolicy

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        r.run();
    }
}

CallerRunsPolicy的做法是在调用execute()的线程中执行。

总结

ThreadPoolExecutor的核心就是Thread集合、BlockingQueue。Thread会从BlockingQueue中不断拉取任务Runnable然后执行,相当于一个生产者和消费者模型。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值