Java併發學習筆記20 線程池 ForkJoinPool

bilibili-Java併發學習筆記20 線程池 ForkJoinPool

基於 java 1.8.0

P64_ForkJoinPool原理與構造方式詳解

  1. 分而治之
  2. 工作竊取

適合 CPU 密集型計算任務,不適合 IO 密集型任務

    /**
     * @param parallelism 並行度級別 默認值 = java.lang.Runtime#availableProcessors
     * @param factory  創建新線程的工廠 默認值 = defaultForkJoinWorkerThreadFactory
     * @param handler  由於執行任務時遇到不可恢復的錯誤而終止的內部工作線程的處理程序 默認值爲 null
     * @param asyncMode
     *      true  爲從未連接的分叉任務建立本地先進先出調度模式。
                在工作線程只處理事件式異步任務的應用程序中,此模式可能比默認的基於本地堆棧的模式更合適。
     *      默認值 = false
     * @throws IllegalArgumentException 如果並行度小於或等於零,或大於實現限制
     * @throws NullPointerException if the factory is null
     * @throws SecurityException if a security manager exists and
     *         the caller is not permitted to modify threads
     *         because it does not hold {@link
     *         java.lang.RuntimePermission}{@code ("modifyThread")}
     */
    public ForkJoinPool(int parallelism,
                        ForkJoinWorkerThreadFactory factory,
                        UncaughtExceptionHandler handler,
                        boolean asyncMode) {
        this(checkParallelism(parallelism),
             checkFactory(factory),
             handler,
             asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
             "ForkJoinPool-" + nextPoolId() + "-worker-");
        checkPermission();
    }

P65_ForkJoinTask詳解與示例分析

public abstract class RecursiveTask<V> extends ForkJoinTask<V> {
    V result;
    protected abstract V compute();
}

public abstract class RecursiveAction extends ForkJoinTask<Void> {
    protected abstract void compute();
}
    /**
     * Submits a ForkJoinTask for execution.
     *
     * @param task the task to submit
     * @param <T> the type of the task's result
     * @return the task
     * @throws NullPointerException if the task is null
     * @throws RejectedExecutionException if the task cannot be
     *         scheduled for execution
     */
    public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
        if (task == null)
            throw new NullPointerException();
        externalPush(task);
        return task;
    }

ForkJoinPool 示例

package new_package.thread.p64;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

public class ForkJoinTest {

    public static void main(String[] args) {
        //ForkJoinPool forkJoinPool = new ForkJoinPool(2);
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        SumTask sumTask = new SumTask(1, 100, 10);
        System.out.println(forkJoinPool.invoke(sumTask));
        forkJoinPool.shutdown();
    }
}

class SumTask extends RecursiveTask<Integer> {

    int limit;
    int start;
    int end;

    public SumTask(int start, int end, int limit) {
        this.start = start;
        this.end = end;
        this.limit = limit;
    }

    @Override
    protected Integer compute() {

        int sum = 0;
        if ((end - start) <= limit) {
            System.out.println(Thread.currentThread().getName());
            for (int i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        }

        // Fork 步驟
        int mid = (end + start) / 2;
        SumTask leftTask = new SumTask(start, mid, limit);
        SumTask rightTask = new SumTask(mid + 1, end, limit);

        // Join 步驟
        invokeAll(leftTask, rightTask);
        Integer leftResult = leftTask.join();
        Integer rightResult = rightTask.join();
        return leftResult + rightResult;
    }
}

P66_CompletionService源碼詳解與示例剖析

// jdk 中 CompletionService 的唯一實現
public class ExecutorCompletionService<V> implements CompletionService<V> {
    private final Executor executor;
    private final AbstractExecutorService aes;
    private final BlockingQueue<Future<V>> completionQueue;
    // ...
}

package new_package.thread.p66;

import java.util.concurrent.*;
import java.util.stream.IntStream;

/**
 * 以任務完成順序獲取到最後的結果集合
 */
public class CompletionServiceTest {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = new ThreadPoolExecutor(10,
                10,
                1,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(),
                (r, executor) -> {

                });
        CompletionService<Integer> completionService = new ExecutorCompletionService(executorService);

        IntStream.range(0, 10).forEach(r -> {
            completionService.submit(() -> {
                try {
                    Thread.sleep((long) (Math.random() * 3000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
                return r + 1;
            });
        });

        for (int i = 0; i < 10; i++) {
            System.out.println(completionService.take().get());
        }

        executorService.shutdown();
    }
}

P67_ThreadLocalRandom在多線程競爭環境下的實現策略

  1. Random
import java.util.Random;

public class RandomTest {

    public static void main(String[] args) {

        Random random = new Random();
        System.out.println(random.nextInt(10));
    }
}
  • 多線程性能問題
    • 多線程同時操作種子更新,產生競爭(自旋鎖)
  • 正確性沒有問題,只是在多線程高併發情況下效率低下
  1. ThreadLocalRandom
import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalRandomTest {

    public static void main(String[] args) {
        ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
        System.out.println(threadLocalRandom.nextInt(10));
    }
}
  • 解決隨機數生成在多線程高併發情況下效率低的問題
  • 在併發代碼中,隨機數生成使用 ThreadLocalRandom 較好

  • 隨機數生成器
    • 隨機數生成器種子
    • 隨機數生成算法
  • 對於 ThreadLocalRandom 來說,其隨機器生成器的種子存放在每個的線程的 ThreadLocal 中
    • Random 是共享同一個種子

ThreadLocalRandom.java

    /**
     * Returns the {@link #current() current} thread's {@code ThreadLocalRandom}.
     * @return the {@link #current() current} thread's {@code ThreadLocalRandom}
     */
    private Object readResolve() {
        return current();
    }

    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long SEED;
    private static final long PROBE;
    private static final long SECONDARY;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> tk = Thread.class;
            SEED = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSeed"));
            PROBE = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomProbe"));
            SECONDARY = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

Thread.java

    // The following three initially uninitialized fields are exclusively
    // managed by class java.util.concurrent.ThreadLocalRandom. These
    // fields are used to build the high-performance PRNGs in the
    // concurrent code, and we can not risk accidental false sharing.
    // Hence, the fields are isolated with @Contended.

    /** The current seed for a ThreadLocalRandom */
    @sun.misc.Contended("tlr")
    long threadLocalRandomSeed;

    /** Probe hash value; nonzero if threadLocalRandomSeed initialized */
    @sun.misc.Contended("tlr")
    int threadLocalRandomProbe;

    /** Secondary seed isolated from public ThreadLocalRandom sequence */
    @sun.misc.Contended("tlr")
    int threadLocalRandomSecondarySeed;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章