Java并发编程之Java线程池
Java线程池:
-
线程池的核心配置参数:
//线程等待任务的超时时间,当线程池的线程个数超过corePoolSize时生效,当线程等待任务的时间超过keepAliveTime时,线程池会停止超过corePoolSize部分的线程。 private volatile long keepAliveTime; //默认false,表示核心线程等待任务不会超时;如果为ture,则允许核心线程超时,也就是说在无任务时,核心线程也会被线程池停止。 private volatile boolean allowCoreThreadTimeOut; //线程池保持的线程最小数量,当allowCoreThreadTimeOut=true时,线程池保持的线程数量为0 private volatile int corePoolSize; //线程池的允许的线程的最大个数,当线程数到达maximumPoolSize时,如果还有新的任务到来线程池会执行拒绝策略 private volatile int maximumPoolSize; //阻塞队列,当线程池的线程数到达corePoolSize时,新接收的任务会放进阻塞队列,阻塞队列又分为有界队列和无界队列;如果是无界队列则任务一直往队列里放,如果是有界队列当任务数超过队列界限时,当corePoolSize < maximumPoolSize时,会启动新的线程。 private final BlockingQueue<Runnable> workQueue; //线程池中保存线程的容器,记录了当前线程池中的线程信息 private final HashSet<Worker> workers = new HashSet<Worker>(); //线程池控制变量,低29位表示线程池的数量,高3位表示线程池状态 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
-
线程池的工作原理: 线程池接收到一个新任务时,会判断当前已经启动的线程个数是否超过corePoolSize,若未超过则启动一个新的线程来处理请求;否则,判断阻塞队列workQueue中的任务个数是否超过上界,若未超过上界,则将任务放入阻塞队列,否则,判断线程池中的当前线程个数是否超过maximumPoolSize,若超过则执行拒绝策略,否则启动新的线程执行任务。
-
线程池的任务提交方法:
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); //判断当前线程池的线程个数 if (workerCountOf(c) < corePoolSize) { //添加一下新的线程处理任务 if (addWorker(command, true)) return; c = ctl.get(); } //如果线程池处于运行状态,将任务放进阻塞队列 if (isRunning(c) && workQueue.offer(command)) { //重新检测线程池的状态,和当前线程数量,防止多线程问题 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); }
-
addWorker方法: 该方法主要用于向线程池中添加新的线程
private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); //检查线程池的状态,如果线程池处于关闭状态并且队列为空则直接返回. if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; //CAS检测线程池的线程数量 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; }
从上面的源码可以看到,向线程池中添加线程的时候,被包装成了Worker
-
线程池的Worker:
//Worker继承自AQS,从阻塞队列中取任务,为啥处理时要进行加锁处理?请看下文解析 private final class Worker extends AbstractQueuedSynchronizer implements Runnable { //运行该worker的线程 final Thread thread; /** Initial task to run. Possibly null. */ Runnable firstTask; //线程执行的任务个数 volatile long completedTasks; /** * 构造函数,构造一个worker */ Worker(Runnable firstTask) { setState(-1); // inhibit interrupts until runWorker this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this); } //线程中执行的方法,该方法是一个循环,在内部会不断获取阻塞队列中的线程并执行 public void run() { runWorker(this); } final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { //循环取阻塞队列中的任务,线程池关闭的时候或者线程池中线程个数超过maximumPoolSize或者获取任务超时的时候getTask()会返回null,退出循环 while (task != null || (task = getTask()) != null) { //此处加锁是因为线程池关闭时会中断空闲的线程,在中断时会尝试获取锁,如果获取不到说明线程正在运行 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 { 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); } } }
-
ThreadPoolExecutor提供了两个方法来关闭线程池:
- shutdown(),关闭线程池,在阻塞队列中的任务会继续执行,但此时线程池不接收新的任务。
- shutdownNow(),立即关闭线程池,不接收新的任务,并尝试停止所有正在执行的任务,该方法是通过Thread.interrupt()来中断执行线程,因此只有响应中断的任务才能通过该方法停止,否则只能等待任务执行完毕。
- 需要说明的是,以上两种停止线程池的方法都不会等待线程池停止完毕,如果要等待线程池停止完毕需要在调用关闭方法之后,再调用awaitTermination方法,该方法会阻塞当前线程一定的时间,直到线程池停止或者当前线程超时。