接着说worker线程是如何工作的。ThreadPoolExecutor有一个成员类叫Worker,所起到的作用就是线程池worker线程的作用。

private final class Worker extends AbstractQueuedSynchronizer implements Runnable

这里AbstractQueuedSynchronizer的作用是使Worker具有锁的功能,在执行任务时,会把Worker锁住,这个时候就无法中断Worker。Worker空闲时候是线程池可以通过获取锁,改变Worker的某些状态,在此期间因为锁被占用,Worker就是不会执行任务的。

Worker工作的逻辑在ThreadPoolExecutor#runWorker方法中

        public void run() {
runWorker(this);
}

因此转到runWorker方法:

    final void runWorker(Worker w) {
Runnable task = w.firstTask;
w.firstTask = null;
boolean completedAbruptly = true;
try {
// 执行分配的任务或者从BlockingQueue中等待获取任务
while (task != null || (task = getTask()) != null) {
w.lock();
clearInterruptsForTaskRun();
try {
// 执行任务之前的工作
beforeExecute(w.thread, task);
Throwable thrown = null;
// 执行任务,如果发生异常,该Worker就不会再继续执行任务
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 {
// Worker不再执行任务的处理,completedAbruptly为false
// 表示正常结束,否则表示执行任务出错。
processWorkerExit(w, completedAbruptly);
}
}

来看看processWorkerExit,重点看看执行任务发生异常时该如何处理

    private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 发生异常,首先要更新Worker数量
if (completedAbruptly)
decrementWorkerCount(); // 移除这个Worker
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
} // 尝试停止线程池,正常运行的线程池调用该方法不会有任何动作
tryTerminate(); int c = ctl.get();
// 如果线程池没有被关闭的话,
if (runStateLessThan(c, STOP)) {
// Worker不是异常退出,检查worker线程数是不是小于最小值
// 这个最小值分为几种情况:
// 1. allowCoreThreadTimeOut(JDK6新加)表示是否允许线程池在超
// 过一定时间没有收到任务后退出,这种情况下,最小值为0,因为如果如
// 果一直没有任何任务,worker线程数是0
// 2. 最小值为corePoolSize,因为corePoolSize可能为0,因此这种情况
// 下,如果有任务的话必然会有Worker,因此最小值为1
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return;
}
// 如果Worker线程数小于最小值,新建一个Worker线程
addWorker(null, false);
}
}

这篇文章主要讲述了Worker线程的工作原理,接下里会讲线程池是如何进行状态切换的。

《java.util.concurrent 包源码阅读》12 线程池系列之ThreadPoolExecutor 第二部分的更多相关文章

  1. 《java.util.concurrent 包源码阅读》 结束语

    <java.util.concurrent 包源码阅读>系列文章已经全部写完了.开始的几篇文章是根据自己的读书笔记整理出来的(当时只阅读了部分的源代码),后面的大部分都是一边读源代码,一边 ...

  2. 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分

    这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...

  3. 《java.util.concurrent 包源码阅读》04 ConcurrentMap

    Java集合框架中的Map类型的数据结构是非线程安全,在多线程环境中使用时需要手动进行线程同步.因此在java.util.concurrent包中提供了一个线程安全版本的Map类型数据结构:Concu ...

  4. 《java.util.concurrent 包源码阅读》02 关于java.util.concurrent.atomic包

    Aomic数据类型有四种类型:AomicBoolean, AomicInteger, AomicLong, 和AomicReferrence(针对Object的)以及它们的数组类型, 还有一个特殊的A ...

  5. 《java.util.concurrent 包源码阅读》17 信号量 Semaphore

    学过操作系统的朋友都知道信号量,在java.util.concurrent包中也有一个关于信号量的实现:Semaphore. 从代码实现的角度来说,信号量与锁很类似,可以看成是一个有限的共享锁,即只能 ...

  6. 《java.util.concurrent 包源码阅读》06 ArrayBlockingQueue

    对于BlockingQueue的具体实现,主要关注的有两点:线程安全的实现和阻塞操作的实现.所以分析ArrayBlockingQueue也是基于这两点. 对于线程安全来说,所有的添加元素的方法和拿走元 ...

  7. 《java.util.concurrent 包源码阅读》09 线程池系列之介绍篇

    concurrent包中Executor接口的主要类的关系图如下: Executor接口非常单一,就是执行一个Runnable的命令. public interface Executor { void ...

  8. 《java.util.concurrent 包源码阅读》22 Fork/Join框架的初体验

    JDK7引入了Fork/Join框架,所谓Fork/Join框架,个人解释:Fork分解任务成独立的子任务,用多线程去执行这些子任务,Join合并子任务的结果.这样就能使用多线程的方式来执行一个任务. ...

  9. 《java.util.concurrent 包源码阅读》24 Fork/Join框架之Work-Stealing

    仔细看了Doug Lea的那篇文章:A Java Fork/Join Framework 中关于Work-Stealing的部分,下面列出该算法的要点(基本是原文的翻译): 1. 每个Worker线程 ...

随机推荐

  1. wpf GifBitmapDecoder 解析 gif 格式

    在网上有很多图片都是gif,那么如何在 wpf 解析 gif? 本文告诉大家如何使用 GifBitmapDecoder 把gif分开为一张一张,获得他的信息. 如果需要把一个 gif 分开,使用的代码 ...

  2. webstorm 卡死解决方法

    方法1: 先在外部终端清空node-modules目录,包括隐藏文件,再打开Webstorm,打开Project Structure页面,选中工程,选择node_modules目录(没有的话自己先新建 ...

  3. 基于JavaBean编辑器读取peroperties文件

    引言 最近在重读<精通Spring+4.x++企业应用开发实战>这本书,看到了有关JavaBean编辑器的部分,了解到PropertyEditor和BeanInfo的使用.不得不说,Bea ...

  4. Redis 学习笔记-5种数据类型的基本操作

    1.string类型 基本操作列表: GET 获取指定键对应的值 SET 设定键值 DEL 删除指定键对应的值(对所有数据类型都有效) > set hello world OK > get ...

  5. [Hdu1342] Lotto

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1342 题目大意:输出6--13从小到大个数,然后按顺序输出6个数,输出所有种可能. 解题思路:这题难度 ...

  6. poi入门之读写excel

    Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能.该篇是介绍poi基本的 ...

  7. CentOS7.3安装NVIDIA-1080ti驱动、cuda、cudnn、TensorFlow

    本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃 Ubuntu非要换centOS...好吧... 看了很多是通过ELRepo源安装驱动,不过我没 ...

  8. mysql 安装配置

    l  下载压缩包: 官网下载地址:https://dev.mysql.com/downloads/mysql/5.1.html#downloads 第一步: 将下载的压缩包解压到你要安装的目录下 第二 ...

  9. java基础解析系列(九)---String不可变性分析

    java基础解析系列(九)---String不可变性分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---In ...

  10. 读书笔记-你不知道的JS上-声明提升

    变量声明提升 Javascript代码一般情况下是由上往下执行的,但是有些情况下不成立. a = 2; //变量声明被提升在当前作用域顶部 var a; console.log(a); console ...