java 线程池的使用及原理(四):线程池的运作原理

了解原理,从使用开始,步步深挖。线程池提交任务分为两种 execute() 和 submit()。让我们一起窥探这并不神秘的原理吧!

1. execute()

  • 由之前的文章可知, new ThreadPoolExecutor() 只是初始化了一些属性,真正的运行,是从 executor() 开始的。

1.1 开始窥探 execute() 的执行原理

1.1.1 execute() 源码如下:

  • execute 主要分为三部分:
    • 第一部分:判断 线程池的线程数workerCountOf© < 核心线程数 corePoolSize,直接添加 Worker
    • 第二部分:第一部分不成立的情况下,Running状态添加一个任务进 工作队列workQueue ,成功后再次检查线程池状态
    • 第三部分:第一部分不成立的情况下,工作队列添加任务失败(队列已满),则尝试添加 Worker 即新线程
public void execute(Runnable command) {
	/* 若空任务,则报空指针异常 */
    if (command == null)
        throw new NullPointerException();
    /* 获取ctl 的值,高位前三位存储 线程池状态,后面29位存储线程个数 */
    int c = ctl.get();
    /* 第一部分:
     *	workerCountOf(c) <==>  return c & CAPACITY; 获取正在执行的线程个数,然后与 corePoolSize 比较是否大于核心线程数
     */
    if (workerCountOf(c) < corePoolSize) {
    	/* 没超过corePoolSize,直接添加任务 */
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    /* 第二部分:
     * 超出 corePoolSize 或者 添加任务 addWorker 失败:
	 * isRunning(c) ==> 检查是否运行状态
	 * workQueue.offer(command) ==> 往队列添加一个任务
	 */
    if (isRunning(c) && workQueue.offer(command)) {
    	/* 
    	* 重新检查线程池状态: 
    	* ! isRunning(recheck) 非Running && remove(command))移除队列任务
    	*  workerCountOf(recheck) == 0 ==> 线程个数为 0,添加一个 null 任务
    	*/
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    /* 第三部分:线程队列也已经满了,尝试添加非核心的线程 */
    else if (!addWorker(command, false)) // 添加任务失败
        reject(command);
}

1.1.2 再来细看 addWorker 如何工作

  • 方法用到比较少见的标记式退出多层循环,可 跳转学习 再继续往下看
  • Worker 就是一个内置Thread对象的Runnable,后面会有具体的解释
  • addWorker 分为两大部分:
    • 第一部分:外层 for 死循环 嵌套 内层 for 死循环
      • 外层 for 循环:主要用于判断线程池的状态,是否有必要继续往下执行
      • 内层 for 循环:主要用于更改 workerCountOf 工作线程数
    • 第二部分:创建一个Worker,并使用内置对象Thread执行任务
/* param1: 线程任务  param2: 是否核心线程 */
private boolean addWorker(Runnable firstTask, boolean core) {
    /* 第一部分 外层 for 死循环 嵌套 内层 for 死循环 */
    retry:
    /* 外层 for 循环 */
    for (;;) {
    	/* 获取ctl值与线程池状态rs */
        int c = ctl.get();
        int rs = runStateOf(c);

        /* 这里逻辑有点绕
* 1. rs >= SHUTDOWN 即非Running,Running 直接退出判断
* 2. rs == SHUTDOWN 不解释,firstTask == null 任务为空,! workQueue.isEmpty() 队列不为空
* 3. !(2) 即 SHUTDOWN 状态下,队列不为空还需要放行,其余STOP、TIDYING、TERMINATED 状态直接返回 false
*/
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;
		/* 内层 for 循环 */
        for (;;) {
        	/* 获取工作的线程池数wc:
        	 * wc >= CAPACITY 是否大于等于能够存储的最大线程数
        	 * wc >= (core ? corePoolSize : maximumPoolSize)) 如果创建的是核心线程,是的话与corePoolSize比较,不是的话与maximumPoolSize比较
        	 */
            int wc = workerCountOf(c);
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            /* CAS 设置 ctl+1,成功直接退出retry循环 */
            if (compareAndIncrementWorkerCount(c))
                break retry;
            /* CAS 失败重新获取ctl,
            * 如果 runStateOf(c) != rs 线程池状态发生变化,continue retry 循环即退出到retry 循环继续执行
            * 否则在内层循环,直至return 或 CAS 更改成功
            */
            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 {
    	/* 创建 Worker 对象,并获取 Thread t */
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
        	/* 使用内置的 ReentrantLock 确保代码同步 */
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                /* 重新获取线程池状态,并判断当前状态是否可以执行任务 */
                int rs = runStateOf(ctl.get());
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    /* 检查线程状态 */
                    if (t.isAlive())
                        throw new IllegalThreadStateException();
                    /* 维护了一个 HashSet<Worker> workers,往set 添加 worker */
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
            	/* start(): 线程开启的地方 */
                t.start();
                workerStarted = true;
            }
        }
    } finally {
    	// 线程池状态发生改变才会执行
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}
/* 移除任务,工作线程-1,尝试终止线程池 */
private void addWorkerFailed(Worker w) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        if (w != null)
            workers.remove(w);
        decrementWorkerCount();
        tryTerminate();
    } finally {
        mainLock.unlock();
    }
}

1.1.3 细谈 Worker 类

  • Worker 实现了 Runnable,又在构造方法 getThreadFactory().newThread(this); 中将 Worker 作为参数创建新线程,所以 thread.start() 会执行 Worker 重写的 run() 方法。
/* 继承AQS,即Worker 也是抽象队列同步器 */
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    /* 不会用到,只是为了屏蔽javac的 warning */
    private static final long serialVersionUID = 6138294804551838833L;
    final Thread thread;
    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.1.4 runWorker(this); 真正执行传入任务的地方

final void runWorker(Worker w) {
    Threadwt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            w.lock();
            /* 根据线程池判断线程是否应该被中断
            * 1. runStateAtLeast(ctl.get(), STOP) <==> return c >= s 即是否非Running && Shutdown,此状态下线程应该被中断 
            * 2. Thread.interrupted() 返回线程中断状态,false 表示没被中断过,立即跳转3,true 表示已经被中断过,继续判断线程池状态
            * 3. 线程池肯定是关闭状态,需要中断当前线程,false 表示没被中断,即应该执行 wt.interrupt();
            */
            if (
            	/* 1. */
            	(runStateAtLeast(ctl.get(), STOP) ||
            	/* 2. */
                 (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)))  		
                  /* 3. */
                  && !wt.isInterrupted())
                wt.interrupt();
            try {
            	/* 空方法,可以看作是预留方法 */
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                	/* 这里的task,就是传入进来的 runnable */
                    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);
    }
}

1.1.5 先来一波小总结

  • 进入 executor(),先进行一系列的判断,得出应该添加哪一种 Worker,即执行哪种 addWorker()

  • addWorker() ,第一部分先判断是否应该添加新线程执行任务,第二部分 w = new Worker(),w.thread.start() 即执行 worker 的 run 方法

  • runWorker 先执行 线程池状态判断,然后执行 w.firstTask.run(),即传入 runnable 的 run 方法,执行完毕,万事大吉。

  • 愉快之余,思考接下来的问题,什么时候执行等待队列workqueue的任务,如何保持核心线程不被销毁。别急,一个一个来。

1.2 workqueue 队列中的如何被执行

  • 从 execute() 方法可知,当 workerCountOf© >= corePoolSize && isRunning© 加入 workQueue,那么,应该是什么方法,从 workQueue 取出任务来
  • 回想整个执行过程,只有 runWorker 中的 getTask() ,貌似与之相关,查看源码,一看还真与 workQueue 相关
private Runnable getTask() {
	// poll获取任务是否超时
    boolean timedOut = false;

    for (;;) {
    	/* 老规矩,获取 ctl 和 线程池状态 */
        int c = ctl.get();
        int rs = runStateOf(c);

        // STOP状态 以上不处理任务 && ShUTDOWN 只处理已添加的任务,即线程池处于关闭状态
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        /* 是否有超时机制timed
		 1. allowCoreThreadTimeOut 允许核心线程空闲超时后回收
		 2. wc > corePoolSize 代表非核心线程空闲均会超时回收
		*/
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
		/* 有四种判断条件的组合
		* 1. wc > maximumPoolSize && wc > 1
		* 2. wc > maximumPoolSize && workQueue.isEmpty()即
		* 3. (timed && timedOut)  && wc > 1
		* 4. (timed && timedOut)  && workQueue.isEmpty()
		*/
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
		/* 根据 timed 的判断,确定获取 workQueue 的任务的方式,后面有详解 */
        try {
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            /* 没能正确获取r,即获取超时 */
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}
  • 小总结:for死循环 一直获取task任务,配合 runWorker 的 while 循环,确保队列的任务会被执行。
1.2.1 LinkedBlockingQueue 类
  • 看一个类,先看构造方法,在看静态属性
  • 构造函数指定队列的允许长度,并创建了一个空节点指向 head、last
public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
    last = head = new Node<E>(null);
}
  • 静态属性
/* 指定版本号 */
private static final long serialVersionUID = -6903933977591709194L;
/* 队列界限 与 Integer原子类 */
private final int capacity;
private final AtomicInteger count = new AtomicInteger();
/* 头节点与尾节点 */
transient Node<E> head;
private transient Node<E> last;

/* 锁 与 Condition */
/* take, poll 等出队锁 */
private final ReentrantLock takeLock = new ReentrantLock();

/* 等待出队的条件唤醒队列 */
private final Condition notEmpty = takeLock.newCondition();

/* put, offer 等进队锁*/
private final ReentrantLock putLock = new ReentrantLock();

/** 等待进队的条件唤醒队列 */
private final Condition notFull = putLock.newCondition();
1.2.2 队列任务进队
  • workQueue.offer()
public boolean offer(E e) {
    if (e ==null) throw new NullPointerException();
    final AtomicInteger count = this.count;
    /* 队列已满,返回 false */
    if (count.get() == capacity)
        return false;
    int c = -1;
    Node<E> node = new Node<E>(e);
    /* 入队锁 */
    final ReentrantLock putLock = this.putLock;
    putLock.lock();
    try {
        if (count.get() < capacity) {
        	/* 入队操作,count++ */
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        }
    } finally {
        putLock.unlock();
    }
    /** 实际执行的是 notEmpty.signal(); */
    if (c == 0)
        signalNotEmpty();
    return c >= 0;
}
1.2.3 队列任务取出的两种方式
  • 1、workQueue.poll(),超出等待时间返回 false
/* param1:0L  param2:时间单位 */
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    E x = null;
    int c = -1;
    long nanos = unit.toNanos(timeout);
    final AtomicInteger count = this.count;
    /* 获取出队锁 */
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        while (count.get() == 0) {
            if (nanos <= 0)
                return null;
            /* 出队notEmpty 等待唤醒 */
            nanos = notEmpty.awaitNanos(nanos);
        }
        /* 取出第一个节点,head 不存储 item */
        x = dequeue();
        /* count-- */
        c = count.getAndDecrement();
        /* 如果 count > 1,即队列不只有一个node,唤醒其他线程来帮忙 */
        if (c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    /* 实际执行的是 notFull.signal(); */
    if (c == capacity)
        signalNotFull();
    return x;
}
  • 2、workQueue.take() ,与 poll() 基本类似,就是少了while 循环 的时间参数
public E take() throws InterruptedException {
    Ex;
    int c = -1;
    final AtomicInteger count = this.count;
    /* 出队锁 */
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        while (count.get() == 0) {
            notEmpty.await();
        }
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    return x;
}

1.3 线程池如何使核心线程一直执行

  • 试想,我们在 new Thread() 的时候,执行完 start() 之后,没进行处理线程自动关闭。那是不是,线程池的线程在执行完任务之后,进行了特殊处理呢?
  • 回看 getTask() 的源码,
/* 1. allowCoreThreadTimeOut 是否允许核心线程过时,默认false
 * 2. wc > corePoolSize; 即只有核心线程,一直执行 workQueue.take();
 */
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
Runnable r = timed ? 
			workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
            workQueue.take();
  • 查看 workQueue.take(); 源码
public E take() throws InterruptedException {
    E x;
    int c = -1;
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
    	/* 核心在这: count == 0,await() 保持睡眠 */
        while (count.get() == 0) {
            notEmpty.await();
        }
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    return x;
}
  • 问题又来了,核心线程阻塞,又是什么时候被唤醒的?
  • 当 workerCountOf© == corePoolSize ,会执行 workQueue.offer(),可以看到,会执行signal() 方法
public boolean offer(E e) {
    if (e == null) throw new NullPointerException();
    final AtomicInteger count = this.count;
    if (count.get() == capacity)
        return false;
    int c = -1;
    Node<E> node = new Node<E>(e);
    final ReentrantLock putLock = this.putLock;
    putLock.lock();
    try {
        if (count.get() < capacity) {
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        }
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
    return c >= 0;
}
  • 能不能设置核心线程超时? 当然可以
    • 注意,设置allowCoreThreadTimeOut(true) ,keepAliveTime 不能为 0,否则报错 IllegalArgumentException: Core threads must have nonzero keep alive times
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class AllowCoreThreadTimeOutTest {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 10,
                1L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(2));
        System.out.println("========== 我来打印点东西 =========");
        threadPool.allowCoreThreadTimeOut(true);
    }
}
  • 可以看到,线程池没有执行shutdown(),依旧被关闭了。
    在这里插入图片描述

  • keepAliveTime 为 0 报错信息
    在这里插入图片描述

1.4 ThreadFactory 线程工厂

static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                              Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                      poolNumber.getAndIncrement() +
                     "-thread-";
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                              namePrefix + threadNumber.getAndIncrement(),
                              0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

2. submit()

  • submit() 有三个重载函数
    • 函数1:Future<?> submit(Runnable task):null 返回值
    • 函数2: Future submit(Runnable task, T result):T 类型返回值
    • 函数3: Future submit(Callable task):Callable 返回值
  • 源码中可以看到,都是先调用 newTaskFor ,返回值再调用 execute()
/* 函数1 */
public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}
/* 函数2 */
public <T> Future<T> submit(Runnable task, T result) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task, result);
    execute(ftask);
    return ftask;
}
/* 函数3 */
public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}
  • 再来看 newTaskFor 源码,有两个重载函数,对应 Runnable、Callable
/* 对应 submit() 函数1、2 */
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}
/* 对应 submit() 函数3 */
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}
  • 总结:submit() 先调用了 newTaskFor() 生成 FutureTask 对象(本质是 Runnable),将 FutureTask 对象当做参数执行 execute(Runnable runnable)。

3. 小总结

  • execute() 中的多线程 是使用 实现 Runnable 方式创建的线程;
  • sumbit() 中的多线程 使用的是 Callable & Future 的方式创建的线程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值