《java.util.concurrent 包源码阅读》24 Fork/Join框架之Work-Stealing
仔细看了Doug Lea的那篇文章:A Java Fork/Join Framework 中关于Work-Stealing的部分,下面列出该算法的要点(基本是原文的翻译):
1. 每个Worker线程都维护一个任务队列,即ForkJoinWorkerThread中的任务队列。
2. 任务队列是双向队列,这样可以同时实现LIFO和FIFO。
3. 子任务会被加入到原先任务所在Worker线程的任务队列。
4. Worker线程用LIFO的方法取出任务,也就后进队列的任务先取出来(子任务总是后加入队列,但是需要先执行)。
5. Worker线程的任务队列为空,会随机从其他的线程的任务队列中拿走一个任务执行(所谓偷任务:steal work,FIFO的方式)。
6. 如果一个Worker线程遇到了join操作,而这时候正在处理其他任务,会等到这个任务结束。否则直接返回。
7. 如果一个Worker线程偷任务失败,它会用yield或者sleep之类的方法休息一会儿,再尝试偷任务(如果所有线程都是空闲状态,即没有任务运行,那么该线程也会进入阻塞状态等待新任务的到来)。
那么重新回到ForkJoinPool的scan方法
private boolean scan(ForkJoinWorkerThread w, int a) {
// scanGuard是32位的整数,用于worker线程数组的索引
// 第16位称为SG_UNIT,为1表示锁住
// 0到15位是mask
int g = scanGuard;
// parallelism表示并发数,一般指CPU可以同时运行的线程数
// 默认值是Runtime类的availableProcessors方法返回值,表示
// 处理器的数量
// a是活跃的Worker线程的数量,parallelism是大于0的,因此
// 条件parallelism == 1 - a满足意味着parallelism为1而a为0
// 而加上blockedCount为0(意味着没有线程因为join被阻塞),
// 两个条件同时满足也就意味既没有任何线程在运行,那么也就
// 意味着没有任务存在于worker线程,所以m=0也就是没法偷任务
// SMASK=0xffff,g & SMASK返回的值scanGuard的0到15位的数值
int m = (parallelism == 1 - a && blockedCount == 0) ? 0 : g & SMASK;
ForkJoinWorkerThread[] ws = workers;
if (ws == null || ws.length <= m)
return false;
//
for (int r = w.seed, k = r, j = -(m + m); j <= m + m; ++j) {
ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
// 从线程队列中随机获取一个worker线程
ForkJoinWorkerThread v = ws[k & m];
// 判断Worker线程是否存在以及该线程的任务队列是否有任务
if (v != null && (b = v.queueBase) != v.queueTop &&
(q = v.queue) != null && (i = (q.length - 1) & b) >= 0) {
// 从队列中偷走一个任务
long u = (i << ASHIFT) + ABASE;
if ((t = q[i]) != null && v.queueBase == b &&
UNSAFE.compareAndSwapObject(q, u, t, null)) {
int d = (v.queueBase = b + 1) - v.queueTop;
v.stealHint = w.poolIndex;
// d是偷走一个任务后任务队列的长度
if (d != 0)
signalWork();
w.execTask(t);
}
r ^= r << 13; r ^= r >>> 17; w.seed = r ^ (r << 5);
// false表示扫描到了任务
return false;
}
else if (j < 0) { // 异或移位,更新k
r ^= r << 13; r ^= r >>> 17; k = r ^= r << 5;
}
else
++k;
}
// 如果扫描不到任务,但是scanGuard被更新了,说明有任务的变化
if (scanGuard != g)
return false;
else {
// 从线程池的任务队列中取出任务来执行
ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
if ((b = queueBase) != queueTop &&
(q = submissionQueue) != null &&
(i = (q.length - 1) & b) >= 0) {
long u = (i << ASHIFT) + ABASE;
if ((t = q[i]) != null && queueBase == b &&
UNSAFE.compareAndSwapObject(q, u, t, null)) {
queueBase = b + 1;
w.execTask(t);
}
return false;
}
return true;
}
}
scan方法的作用就是从其他线程的任务队列中偷任务。
《java.util.concurrent 包源码阅读》24 Fork/Join框架之Work-Stealing的更多相关文章
- 《java.util.concurrent 包源码阅读》 结束语
<java.util.concurrent 包源码阅读>系列文章已经全部写完了.开始的几篇文章是根据自己的读书笔记整理出来的(当时只阅读了部分的源代码),后面的大部分都是一边读源代码,一边 ...
- 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分
这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...
- 《java.util.concurrent 包源码阅读》02 关于java.util.concurrent.atomic包
Aomic数据类型有四种类型:AomicBoolean, AomicInteger, AomicLong, 和AomicReferrence(针对Object的)以及它们的数组类型, 还有一个特殊的A ...
- 《java.util.concurrent 包源码阅读》04 ConcurrentMap
Java集合框架中的Map类型的数据结构是非线程安全,在多线程环境中使用时需要手动进行线程同步.因此在java.util.concurrent包中提供了一个线程安全版本的Map类型数据结构:Concu ...
- 《java.util.concurrent 包源码阅读》17 信号量 Semaphore
学过操作系统的朋友都知道信号量,在java.util.concurrent包中也有一个关于信号量的实现:Semaphore. 从代码实现的角度来说,信号量与锁很类似,可以看成是一个有限的共享锁,即只能 ...
- 《java.util.concurrent 包源码阅读》06 ArrayBlockingQueue
对于BlockingQueue的具体实现,主要关注的有两点:线程安全的实现和阻塞操作的实现.所以分析ArrayBlockingQueue也是基于这两点. 对于线程安全来说,所有的添加元素的方法和拿走元 ...
- 《java.util.concurrent 包源码阅读》22 Fork/Join框架的初体验
JDK7引入了Fork/Join框架,所谓Fork/Join框架,个人解释:Fork分解任务成独立的子任务,用多线程去执行这些子任务,Join合并子任务的结果.这样就能使用多线程的方式来执行一个任务. ...
- 《java.util.concurrent 包源码阅读》09 线程池系列之介绍篇
concurrent包中Executor接口的主要类的关系图如下: Executor接口非常单一,就是执行一个Runnable的命令. public interface Executor { void ...
- 《java.util.concurrent 包源码阅读》25 Fork/Join框架之Fork与Work-Stealing(重写23,24)
在写前面两篇文章23和24的时候自己有很多细节搞得不是很明白,这篇文章把Fork和Work-Stealing相关的源代码重新梳理一下. 首先来看一些线程池定义的成员变量: 关于scanGuard: v ...
随机推荐
- win10 uwp 后台获取资源
本文告诉大家,从后台代码获取界面定义的资源. 如果一个资源是写在 App 的资源,那么如何使用代码去获得他? 简单的方法是使用下面的代码 Application.Current.Resources[& ...
- Python学习笔记(七)
Python学习笔记(七): 深浅拷贝 Set-集合 函数 1. 深浅拷贝 1. 浅拷贝-多层嵌套只拷贝第一层 a = [[1,2],3,4] b = a.copy() print(b) # 结果:[ ...
- STM32 AD采样电压计算公式
在使用STM32的ADC进行检测电压时必须回涉及到电压值的计算,为了更高效率的获取电压,现在有以下三种方法: 你得到的结果是你当前AD引脚上的电压值相对于3.3V和4096转换成的数字.假如你得到的A ...
- java 入门之八大内置基本类型
本文采用eclipse 工具演示,如果您对eclipse 工具不了解,请先学习下 eclipse 工具的使用,这个里面只是简单的介绍下输出和注释: 安装完成eclipse 以后,双击进入 后一次点击 ...
- mybatis mysql 批量插入
场景描述: 使用mybatis操作mysql数据库,进行批量插入数据,提高代码质量和执行效率. 环境: mybatis spring mysql java xml配置文件 <insert id ...
- js文件中使用EL表达式的问题
var str = '${str}' ; var str = '${obj.属性名}'; 只可以再jsp页面的<script></script>中使用,外部引入的js文件中不能 ...
- LeetCode 280. Wiggle Sort (摆动排序)$
Given an unsorted array nums, reorder it in-place such that nums[0] <= nums[1] >= nums[2] < ...
- 基于场景解析RecyclerView的回收复用机制原理
最近在研究 RecyclerView 的回收复用机制,顺便记录一下.我们知道,RecyclerView 在 layout 子 View 时,都通过回收复用机制来管理.网上关于回收复用机制的分析讲解的文 ...
- JDBC(三)数据库连接池(DBCP、C3P0)
前言 这段时间状态有一点浮躁,希望自己静下心来.还有特别多的东西还没有学懂.需要学习的东西非常的多,加油! 一.JDBC复习 Java Data Base Connectivity,java数据库连接 ...
- Java基础笔记1
java (开源,跨操作系统)j2ee jre java基础 javaoop java高级 JDK(JAVA developer Kitool): java开发工具 (开发人员使用) JRE(java ...