在Java并发编程中,有两个工具平时开发用的少,但是在很多应用框架都有有他们的身影——ThreadLocal和Fork/Join。ThreadLocal用于解决线程安全问题,多线程隔离,里面运用到了黄金分割和斐波那契散列。Fork/Join用于任务的拆分计算和结果聚合,功能类似于Hadoop里面的MapReduce。
初识ThreadLocal
ThreadLocal解决每个线程对共享变量的相关操作仅当前线程可见。每一个线程应该有一个单独空间,用来存储共享变量的副本,每个线程对共享变量副本进行操作。
ThreadLocal内部提供了4个主要的方法,分别如下:
- set(T value),设置一个value,保存到当前线程的副本。
- get(),得到当前线程内保存的value
- remove(),移除当前线程内保存的value
- withInitial,Java8提供的方法,使用函数式接口来完成初始化的设置
public class ThreadLocalExample {
public final static ThreadLocal<String> STRING_THREAD_LOCAL = ThreadLocal.withInitial(() -> "DEFAULT VALUE");
public static void main(String[] args) throws InterruptedException {
System.out.println(Thread.currentThread().getName() + ":INITIAL_VALUE->" + STRING_THREAD_LOCAL.get());
STRING_THREAD_LOCAL.set("Main Thread Value");
System.out.println(Thread.currentThread().getName() + ":BEFORE->" + STRING_THREAD_LOCAL.get());
Thread t1 = new Thread(() -> {
String value = STRING_THREAD_LOCAL.get();
if (null == value) {
STRING_THREAD_LOCAL.set("T1 Thread Value");
}
System.out.println(Thread.currentThread().getName() + ":T1->" + STRING_THREAD_LOCAL.get());
}, "t1");
Thread t2 = new Thread(() -> {
String value = STRING_THREAD_LOCAL.get();
if (null == value) {
STRING_THREAD_LOCAL.set("T2 Thread Value");
}
System.out.println(Thread.currentThread().getName() + ":T2->" + STRING_THREAD_LOCAL.get());
}, "t2");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(Thread.currentThread().getName() + ":AFTER->" + STRING_THREAD_LOCAL.get());
}
}
main:INITIAL_VALUE->DEFAULT VALUE
main:BEFORE->Main Thread Value
t1:T1->DEFAULT VALUE
t2:T2->DEFAULT VALUE
main:AFTER->Main Thread Value
SimpleDateFormatExample
线程不安全的例子
public class SimpleDateFormatExample {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static Date parse(String strDate) throws ParseException {
return sdf.parse(strDate);
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 9; i++) {
executorService.execute(()->{
try{
System.out.println(parse("2023-06-30 16:20:56"));
}catch (ParseException e){
e.printStackTrace();
}
});
}
}
}
线程安全的例子
public class SimpleDateFormatExample {
private static final String DATEFORMAT = "yyyy-MM-dd HH:mm:ss";
private static ThreadLocal<DateFormat> dateFormatThreadLocal = new ThreadLocal<>();
private static DateFormat getDateFormat(){
DateFormat df = dateFormatThreadLocal.get();
if (null == df) {
df = new SimpleDateFormat(DATEFORMAT);
dateFormatThreadLocal.set(df);
}
return df;
}
public static Date parse(String strDate) throws ParseException {
return getDateFormat().parse(strDate);
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 9; i++) {
executorService.execute(()->{
try{
System.out.println(parse("2023-06-30 16:20:56"));
}catch (ParseException e){
e.printStackTrace();
}
});
}
}
}
源码分析
ThreadLocal采用的是延迟加载,即第一次set的时候才会创建对象
// 赋值操作
public void set(T value) {
// 获取当前线程
Thread t = Thread.currentThread();
// 根据当前线程获取一个ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果Map存在则进入赋值逻辑
if (map != null)
map.set(this, value);
else // 否则创建Map对象
createMap(t, value);
}
// 获取当前线程的成员变量
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// 创建一个ThreadLocalMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
// 创建ThreadLocalMap的步骤
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
// INITIAL_CAPACITY = 16 初始化一个长度为16的Entry数组
table = new Entry[INITIAL_CAPACITY];
// firstKey.threadLocalHashCode根据key获取hashCode 通过位运算进行对INITIAL_CAPACITY取模,计算出i的值,即数组下标
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// 把ThreadLocal的值保存在指定位置
table[i] = new Entry(firstKey, firstValue);
size = 1;
// 设置扩容阈值
setThreshold(INITIAL_CAPACITY);
}
// 获取hashCode
private final int threadLocalHashCode = nextHashCode();
// 先获取值,在做加法运算
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
// 原子性的Integer
private static AtomicInteger nextHashCode = new AtomicInteger();
// 魔数 https://www.javaspecialists.eu/archive/Issue164-Why-0x61c88647.html
private static final int HASH_INCREMENT = 0x61c88647;
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
// 取模求出下一个下标位置
int i = key.threadLocalHashCode & (len-1);
//
for (Entry e = tab[i];
e != null;
// 通过线性探测获取i的值,主要为解决hash冲突
e = tab[i = nextIndex(i, len)]) {
// 获取ThreadLocal
ThreadLocal<?> k = e.get();
// 如果key存在,则直接替换key的值
if (k == key) {
e.value = value;
return;
}
// 如果key不存在,做替换清理Entry的动作
if (k == null) {
//
replaceStaleEntry(key, value, i);
return;
}
}
// 赋值操作
tab[i] = new Entry(key, value);
// 累加做一个统计
int sz = ++size;
// 清理槽位,如果当前元素个数大于阈值时,则需要做扩容
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
线性探测,是用来解决hash冲突的一种策略,是一种开放寻址的策略。
hash表,是根据key进行直接访问的数据结构,也就是说,我们可以通过hash函数把key映射到hash表一个位置来访问,从而加快查找的速度。存放记录的数据就是hash表(散列表)。
当我们针对一个key通过hash函数计算产生位置时,如果存在这个位置已经被占有,则认为存在hash冲突,线性探测可以解决冲突问题,这里分为两种情况:
写入:查找hash表中离冲突单元最近的空闲单元,把馨的键值插入到这个空闲单元。
查找:从根据hash函数计算得到的位置开始往后查找,直到找到与key对应的value或者空闲单元。
// key value staleSlot = i
private void replaceStaleEntry(ThreadLocal<?> key, Object value,
int staleSlot) {
Entry[] tab = table;
int len = tab.length;
Entry e;
int slotToExpunge = staleSlot;
// 往前查找 找到最前的无效的slot
for (int i = prevIndex(staleSlot, len); // case 1
(e = tab[i]) != null;
i = prevIndex(i, len))
if (e.get() == null)
slotToExpunge = i;
// 往后查找 找到最后一个Entry (线性探测) case 2
for (int i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
// 如果找到了匹配的key
if (k == key) {
// 更新对应的slot的值
e.value = value;
// 与无效的slot进行位置替换
tab[i] = tab[staleSlot];
tab[staleSlot] = e;
// 如果最前面的无效的slot与当前的staleSlot相等,则i的位置作为清理的起点 case
if (slotToExpunge == staleSlot)
slotToExpunge = i;
//
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
return;
}
// 如果当前slot已经是无效的,并且往前扫描没有无效的slot,则更新slotToExpunge为i的值
if (k == null && slotToExpunge == staleSlot)
slotToExpunge = i;
}
// case 3
// 如果key对应value在Entry中不存在,则直接放一个新的Entry
tab[staleSlot].value = null;
tab[staleSlot] = new Entry(key, value);
// 没有任何无效的slot,则做一次清理
if (slotToExpunge != staleSlot)
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}
- 向前查找发现有失效的Entry,向后查找到可以覆盖的Entry
- 向前查找发现有失效的Entry,向后查找未找到可以覆盖的Entry
- 向前未找到失效的Entry,向后找到可以覆盖的Entry
- 向前未找到失效的Entry,向后未找到可以覆盖的Entry
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// 直接把当前位置直接清理掉
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
// Rehash until we encounter null
Entry e;
int i;
// 往下查找清理的Entry
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
// 如果key不存在,则直接清理
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {// 如果key存在
// 重新计算下标
int h = k.threadLocalHashCode & (len - 1);
// 如果当前下标与i不一致
if (h != i) {
// 把i的清除
tab[i] = null;
// 如果存在hash冲突,找下一个h即可
while (tab[h] != null)
h = nextIndex(h, len);
// 把e给tab[h]
tab[h] = e;
}
}
}
return i;
}
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
// 获取数组table
Entry[] tab = table;
// 获取数组长度
int len = tab.length;
do {
i = nextIndex(i, len);
Entry e = tab[i];
if (e != null && e.get() == null) {
n = len;
removed = true;
i = expungeStaleEntry(i);
}
// n>>>=1 16->8->4->2->1
// 正常实现:循环所有的for(i=0;i<n;i++) 问题:插入的过程中性能下降。
// for(i=0;i<n;i++)->while ( (n >>>= 1) != 0) O(n) -> O(log2n)
} while ( (n >>>= 1) != 0);
return removed;
}
public T get() {
// 先获取到当前线程
Thread t = Thread.currentThread();
// 获取到ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 如果获取不到对应的值,则进行初始化
return setInitialValue();
}
// 初始化
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
关于ThreadLocal中的弱引用
// ThreadLocal里关于Entry的定义
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
当JVM进行垃圾回收时,无论内存是否充足都会回收被弱引用关联的对象,在Java中用java.lang.refWeakReference表示
弱引用的例子
public class WeekReferenceExample {
static Object object = new Object();
public static void main(String[] args) {
WeakReference<Object> objectWeakReference = new WeakReference<>(object);
object = null;
System.gc();
System.out.println("gc之后"+objectWeakReference.get());
}
}
强引用的例子
public class StrongReferenceExample {
static Object object = new Object();
public static void main(String[] args) {
Object strongRef = object;
object = null;
System.gc();
System.out.println("gc之后"+strongRef);
}
}
如果key采用强引用,会导致对象被回收但是ThreadLocalMap不被回收的现象,导致内存溢出。
如果key采用弱引用,也会有内存泄漏的风险,但是相比之下,处理起来更加简单。
TheadLocal内存泄漏
使用不当,ThreadLocal是否会导致内存泄漏呢?答案是会。
ThreadLocal在设计上如何避免内存泄漏的:
- replaceStaleEntry(),会清理当前key所在位置相邻的失效Entry。
- ThreadLocalMap的key采用弱引用,在ThreadLocal实例被回收之后,key的引用指向为null,可以触发replaceStaleEntry中的线性探测进行回收。
// ThreadLocal导致内存泄漏的场景 ---- jconsole进行数据监控
public class MemoryLeakExample {
static class LocalVariable {
private Long[] data = new Long[1024 * 1024];
}
static ThreadPoolExecutor service = new ThreadPoolExecutor(5, 5, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
static ThreadLocal local = new ThreadLocal();
public static void main(String[] args) throws InterruptedException {
// 延迟启动,方便jconsole进行数据监控
TimeUnit.SECONDS.sleep(8);
CountDownLatch countDownLatch = new CountDownLatch(50);
for (int i = 0; i < 50; i++) {
service.execute(()->{
local.set(new LocalVariable());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 移除当前线程存储的value
// local.remove();
countDownLatch.countDown();
});
}
countDownLatch.await();
local = null;
// 主动调用gc
System.gc();
System.out.println(local);
}
}
总得来说,在使用ThreadLocal的地方,每个线程用完之后,最终需要调用remove()方法防止内存泄漏
任务拆分与聚合Fork/Join
Fork/Join是JDK1.7提供的一个任务拆分与聚合的工具,它可以把一个大任务拆分成多个子任务进行并行计算,再把拆分的子任务的计算结果进行合并。简单来说,Fork/Join的核心思想就是分而治之,Fork用来分解任务,Join用来实现数据聚合。
应用开发很少用,但是常见的组件都有:比如CompletableFuture、Java8中并发流parallelStream等。
Fork/Join的核心源码分析
- 提交任务后(submit),把任务随机保存在WorkQueue数组的偶数位。
- 开启或者唤醒ForkJoinWorkerThread线程从WorkQueue数组中获取ForkJoinTask任务。如果当前线程是新创建的,则需要绑定一个队列,后续该线程默认会消费该队列中的任务。
- 启动线程并从绑定的WorkQueue中获取任务,如果当前线程绑定的队列中没有任务,则会从其他队列中窃取任务并执行。
- 执行具体任务时会调用ForkJoinTask.exec()方法,exec()是一个抽象方法,具体的实现是由子类完成的。比如RecursiveAction子类,实现了exec()抽象方法,并调用compute()方法进行计算。
Fork/Join的核心API说明
Fork/Join中的具体的任务用ForkJoinTask类来表示,ForkJoinTask里有如下几个重要的方法。
- fork(),创建一个异步执行的子任务。
- join(),等待任务完成后返回计算结果。
- invoke(),开始执行任务,必须等待其执行结果。
常见的两个子类实现:RecursiveAction(无返回结果的任务)和RecursiveTask(有返回结果的任务)
针对与ForkJoinTask的线程池是ForkJoinPool和ForkJoinWorkerThread
- invoke(ForkJoinTask task),提交任务并且一直组设,知道任务执行完成返回合并结果。
- execute(ForkJoinTask<?> task),异步执行任务,无返回值。
- submit(ForkJoinTask task),异步执行任务,返回Task本身,可以通过task.get()方法回去合并之后的结果。
需要注意,ForkJoinTask在不显示使用ForkJoinPool.execute/invoke/submit()方法执行的情况下,也可以使用自己的fork/invoke()方法执行。
Fork/Join的基本使用
public class ForkJoinExample {
private static final Integer MAX = 400;
static class CalculationTask extends RecursiveTask<Integer>{
private Integer start;
private Integer end;
public CalculationTask(Integer start, Integer end) {
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
if (end - start < MAX){
System.out.println("开始计算的部分:start = " + start + "; end = "+ end);
Integer total = 0;
for (int index = this.start; index <= this.end;index ++){
total += index;
}
return total;
}
return createSubtasks();
}
private Integer createSubtasks() {
CalculationTask subTask1 = new CalculationTask(start, (start + end) / 2);
subTask1.fork();
CalculationTask subTask2 = new CalculationTask((start + end) / 2 + 1, end);
subTask2.fork();
return subTask1.join() + subTask2.join();
}
}
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> taskFuture = pool.submit(new CalculationTask(1, 2023));
try {
Integer result = taskFuture.get();
System.out.println("result:"+result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
Fork/Join的实现原理
每个线程都维护了一个私有的WorkQueue,Fork/Join使用的是后进先出的方式从WorkQueue取出任务并处理。采用的是工作窃取算法(work-stealing)。
工作窃取算法:就是某个线程自己的任务处理结束之后,会从其他线程的工作队列中窃取任务来执行。
源码解析
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
if (task == null)
throw new NullPointerException();
//
externalPush(task);
return task;
}
// 设计者考虑到如果在线程竞争不激烈的时候,直接添加。如果线程竞争激烈的时候则去进行额外的处理
final void externalPush(ForkJoinTask<?> task) {
WorkQueue[] ws; WorkQueue q; int m;
// 线程安全的获取一个int类型的Probe[探针]
int r = ThreadLocalRandom.getProbe();
// 可锁定的状态/运行状态
int rs = runState;
if ((ws = workQueues) != null && (m = (ws.length - 1)) >= 0 &&
(q = ws[m & r & SQMASK]) != null && r != 0 && rs > 0 &&
// 考虑到可能是多线程进行添加,所以加锁
U.compareAndSwapInt(q, QLOCK, 0, 1)) {
ForkJoinTask<?>[] a; int am, n, s;
if ((a = q.array) != null &&
(am = a.length - 1) > (n = (s = q.top) - q.base)) {
// 获取一个偏移量
int j = ((am & s) << ASHIFT) + ABASE;
// putOrderedObject()方法在指定对象a中的内存的偏移量位置,并赋予a一个新的元素
U.putOrderedObject(a, j, task);// 把任务添加到当前WorkQueue的ForkJoinTask数组
U.putOrderedInt(q, QTOP, s + 1);// 更新QTOP索引
U.putIntVolatile(q, QLOCK, 0); // 释放锁
if (n <= 1) // 当前队列的任务处理完毕,工作线程可能处于等待状态
signalWork(ws, q); // 唤醒或者创建工作线程
return;
}
U.compareAndSwapInt(q, QLOCK, 1, 0);
}
externalSubmit(task);
}
private void externalSubmit(ForkJoinTask<?> task) {
int r;
// 得到一个探针hash值
if ((r = ThreadLocalRandom.getProbe()) == 0) {
//
ThreadLocalRandom.localInit();
r = ThreadLocalRandom.getProbe();
}
// 自旋
for (;;) {
WorkQueue[] ws; WorkQueue q; int rs, m, k;
boolean move = false;
if ((rs = runState) < 0) { // case 0
tryTerminate(false, false);
throw new RejectedExecutionException();
}
// case 1 初始化
else if ((rs & STARTED) == 0 ||
((ws = workQueues) == null || (m = ws.length - 1) < 0)) {
int ns = 0;
// 获取到锁
rs = lockRunState();
try {
if ((rs & STARTED) == 0) {
U.compareAndSwapObject(this, STEALCOUNTER, null,
new AtomicLong());
//
int p = config & SMASK;
// 初始化是n=1
int n = (p > 1) ? p - 1 : 1;
// 往文档下发查找
n |= n >>> 1; n |= n >>> 2; n |= n >>> 4;
n |= n >>> 8; n |= n >>> 16; n = (n + 1) << 1;
// 初始化一个workQueues[4]
workQueues = new WorkQueue[n];
// ns = 4
ns = STARTED;
}
} finally {
// 释放锁
unlockRunState(rs, (rs & ~RSLOCK) | ns);
}
}
// case 2
// 从126里面获取偶数位的下标的位置workQueue
else if ((q = ws[k = r & m & SQMASK]) != null) {
// 通过CAS加锁
if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) {
ForkJoinTask<?>[] a = q.array;
int s = q.top;
boolean submitted = false; // initial submission or resizing
try { // locked version of push
// 如果ForkJoinTask[]数组不为空,则计算偏移量,把任务存储到数组指定位置,修改索 引
if ((a != null && a.length > s + 1 - q.base) ||
(a = q.growArray()) != null) {
int j = (((a.length - 1) & s) << ASHIFT) + ABASE;
U.putOrderedObject(a, j, task);
U.putOrderedInt(q, QTOP, s + 1);
submitted = true;
}
} finally {
// 释放锁
U.compareAndSwapInt(q, QLOCK, 1, 0);
}
if (submitted) {
// 任务提交成功后,唤醒或者创建工作线程并执行
signalWork(ws, q);
return;
}
}
move = true; // move on failure
}
// case 3 workQueue 未初始化
else if (((rs = runState) & RSLOCK) == 0) { // create new queue
q = new WorkQueue(this, null);
q.h int = r;
q.config = k | SHARED_QUEUE;
q.scanState = INACTIVE;
rs = lockRunState(); // publish index
if (rs > 0 && (ws = workQueues) != null &&
k < ws.length && ws[k] == null)
ws[k] = q; // else terminated
unlockRunState(rs, rs & ~RSLOCK);
}
else
move = true; // move if busy
if (move)
r = ThreadLocalRandom.advanceProbe(r);
}
}
public final ForkJoinTask<V> fork() {
Thread t;
if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
((ForkJoinWorkerThread)t).workQueue.push(this);
else
ForkJoinPool.common.externalPush(this);
return this;
}
1.初始化WorkQueues
2.初始化WorkQueue
3.往WorkQueue添加Task
1.拒绝
1.更新探针
4 100
010
110
001
110
111
(7+1)*2 =16
如果是单线程下的设计一般取的是距离n最近的2的n次幂
// 获取左边距离n最近的2的n+1次幂
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
n = (n + 1) << 1;
// 普通的写法 获取左边距离n最近的2的n次幂
4
100
110
111
5
7+1 = 8
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
为什么多线程中,初始化容器的大小要比传入的大,比单线程的正好大2倍?
作者说:在设计初衷,因为考虑到是多线程的场景,所以并不希望因为频繁的扩容,导致性能的损耗,所以在初始化过程中选择一种膨胀阈值设计。
volatile long ctl; // main pool control
// 可锁定的状态/运行状态
volatile int runState; // lockable status
// 有可能16个1都有作用
final int config; // parallelism, mode
int indexSeed; // to generate worker index
// 初始化一个工作队列
volatile WorkQueue[] workQueues; // main registry
final ForkJoinWorkerThreadFactory factory;
final UncaughtExceptionHandler ueh; // per-worker UEH
final String workerNamePrefix; // to create worker name string
volatile AtomicLong stealCounter; // also used as sync monitor
private static final sun.misc.Unsafe U;
private static final int ABASE;
private static final int ASHIFT;
private static final long CTL;
private static final long RUNSTATE;
private static final long STEALCOUNTER;
private static final long PARKBLOCKER;
private static final long QTOP;
private static final long QLOCK;
private static final long QSCANSTATE;
private static final long QPARKER;
private static final long QCURRENTSTEAL;
private static final long QCURRENTJOIN;
static {
// initialize field offsets for CAS etc
try {
U = sun.misc.Unsafe.getUnsafe();
Class<?> k = ForkJoinPool.class;
CTL = U.objectFieldOffset
(k.getDeclaredField("ctl"));
RUNSTATE = U.objectFieldOffset
(k.getDeclaredField("runState"));
STEALCOUNTER = U.objectFieldOffset
(k.getDeclaredField("stealCounter"));
Class<?> tk = Thread.class;
PARKBLOCKER = U.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
Class<?> wk = WorkQueue.class;
QTOP = U.objectFieldOffset
(wk.getDeclaredField("top"));
QLOCK = U.objectFieldOffset
(wk.getDeclaredField("qlock"));
QSCANSTATE = U.objectFieldOffset
(wk.getDeclaredField("scanState"));
QPARKER = U.objectFieldOffset
(wk.getDeclaredField("parker"));
QCURRENTSTEAL = U.objectFieldOffset
(wk.getDeclaredField("currentSteal"));
QCURRENTJOIN = U.objectFieldOffset
(wk.getDeclaredField("currentJoin"));
Class<?> ak = ForkJoinTask[].class;
ABASE = U.arrayBaseOffset(ak);
int scale = U.arrayIndexScale(ak);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (Exception e) {
throw new Error(e);
}
commonMaxSpares = DEFAULT_COMMON_MAX_SPARES;
defaultForkJoinWorkerThreadFactory =
new DefaultForkJoinWorkerThreadFactory();
modifyThreadPermission = new RuntimePermission("modifyThread");
common = java.security.AccessController.doPrivileged
(new java.security.PrivilegedAction<ForkJoinPool>() {
public ForkJoinPool run() { return makeCommonPool(); }});
int par = common.config & SMASK; // report 1 even if threads disabled
commonParallelism = par > 0 ? par : 1;
}
private static final int RSLOCK = 1;
private static final int RSIGNAL = 1 << 1;
// 100 ==>4 00 01 10 11
private static final int STARTED = 1 << 2;
private static final int STOP = 1 << 29;
private static final int TERMINATED = 1 << 30;
private static final int SHUTDOWN = 1 << 31;
private int lockRunState() {
int rs;
return ((((rs = runState) & RSLOCK) != 0 ||
!U.compareAndSwapInt(this, RUNSTATE, rs, rs |= RSLOCK)) ?
awaitRunStateLock() : rs);
}
private int awaitRunStateLock() {
Object lock;
boolean wasInterrupted = false;
for (int spins = SPINS, r = 0, rs, ns;;) {
if (((rs = runState) & RSLOCK) == 0) {
if (U.compareAndSwapInt(this, RUNSTATE, rs, ns = rs | RSLOCK)) {
if (wasInterrupted) {
try {
Thread.currentThread().interrupt();
} catch (SecurityException ignore) {
}
}
return ns;
}
}
else if (r == 0)
r = ThreadLocalRandom.nextSecondarySeed();
else if (spins > 0) {
r ^= r << 6; r ^= r >>> 21; r ^= r << 7; // xorshift
if (r >= 0)
--spins;
}
else if ((rs & STARTED) == 0 || (lock = stealCounter) == null)
Thread.yield(); // initialization race
else if (U.compareAndSwapInt(this, RUNSTATE, rs, rs | RSIGNAL)) {
synchronized (lock) {
if ((runState & RSIGNAL) != 0) {
try {
lock.wait();
} catch (InterruptedException ie) {
if (!(Thread.currentThread() instanceof
ForkJoinWorkerThread))
wasInterrupted = true;
}
}
else
lock.notifyAll();
}
}
}
}
// 00000000000000001111111111111111 十进制数为:65535
static final int SMASK = 0xffff;
// 00000000000000000111111111111111 十进制数为:32767
static final int MAX_CAP = 0x7fff;
// 00000000000000001111111111111110 十进制数为:65534
static final int EVENMASK = 0xfffe;
// 00000000000000000000000001111110 十进制数为:126
static final int SQMASK = 0x007e;
static final void localInit() {
// 斐波那契散列值,黄金分割数
int p = probeGenerator.addAndGet(PROBE_INCREMENT);
// 获取探针
int probe = (p == 0) ? 1 : p;
// 加密获取信息指纹
long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
// 获取当前线程
Thread t = Thread.currentThread();
// 设置信息指纹和探针
UNSAFE.putLong(t, SEED, seed);
UNSAFE.putInt(t, PROBE, probe);
}
private static final int PROBE_INCREMENT = 0x9e3779b9;
private static final int HASH_INCREMENT = 0x61c88647;
Random:生成随机数(线性同余法,1958,伪随机数生成法)