并发编程不得不知的工具

在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
                  1117+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,伪随机数生成法)

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DougLiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值