12.线程池


Java 中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

1. 线程池复用线程的原理

  1. 用户提交的不是线程是Runnable接口的实现类
  2. 每个Worker都是一个Runnable接口的实例
  3. 每个Worker对象都对应一个线程,该线程的Runnable是Worker本身
     private final class Worker extends AbstractQueuedSynchronizer implements Runnable {// Worker本身就是Runnable的实现类
        public void run() {
            runWorker(this);
        }
    }
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this); // 新建一个线程,里面的Runnable是work本身
    }
  1. Worker里面的的thread线程会被线程池启动,它就是线程池里真正运行的线程
  2. worker线程里是一个死循环,重复取出用户提交的Runnable,调用它的run方法
    private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
        public void run() {
            runWorker(this);
        }
    }

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run(); // 执行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 {
            processWorkerExit(w, completedAbruptly);
        }
    }

2. 线程池处理流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JgY6ENo9-1578021304331)(4695305B9CD44E3E84F879EA1F8DEC9A)]

  1. 如果当前运行的线程少于 corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。

  2. 如果运行的线程等于或多于 corePoolSize,则将任务加入 BlockingQueue。

  3. 如果无法将任务加入 BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。

  4. 如果创建新线程将使当前运行的线程超出 maximumPoolSize,任务将被拒绝,并调用 RejectedExecutionHandler.rejectedExecution() 方法。

3. 线程池的使用

3.1. 线程池的创建

public class ThreadPoolTest {
    public static void main(String[] args) {
        int corePoolSize = 10;
        int maximumPoolSize = 20;
        long keepAliveTime = 10;
        TimeUnit unit = TimeUnit.MINUTES;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue(100);
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue);
        for (int i = 0; i < 15; i++) {
            int t = i;
            threadPoolExecutor.execute(()->{
                System.out.println("Thread :" + t);
            });
        }

    }
}

参数说明:

  • int corePoolSize 核心线程数
  • int maximumPoolSize 最大线程数
  • long keepAliveTime 线程池的工作线程空闲后,保持存活的时间
  • TimeUnit unit 存活时间的单位
  • BlockingQueue workQueue 任务队列
  • ThreadFactory threadFactory 线程工厂
  • RejectedExecutionHandler handler 拒绝策略

3.1.1. 可用任务队列

  • ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。

  • LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按 FIFO 排序元素,吞吐量通常要高于 ArrayBlockingQueue。静态工厂方法 Executors.newFixedThreadPool() 使用了这个队列。

  • SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 Linked-BlockingQueue,静态工厂方法 Executors.newCachedThreadPool 使用了这个队列。

  • PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

3.1.2. 线程工厂

用于初始化线程,可以设置线程名等一些信息

3.1.3. 拒绝处理器

队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是 AbortPolicy,表示无法处理新任务时抛出异常

  • AbortPolicy:直接抛出异常
  • CallerRunsPolicy:只用调用者所在线程来运行任务
  • DiscardOldestPolicy:丢弃队列里最老的一个任务,并执行当前任务
  • DiscardPolicy:不处理,丢弃掉

3.1.4. 超时处理

当线程空闲超过门限值,就会被回收,我们知道线程池里的线程是获取任务,获取成功就执行。

while (task != null || (task = getTask()) != null)

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    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 {
            processWorkerExit(w, completedAbruptly);
        }
    }

获取任务调用了getTask()方法,该方法会阻塞住获取任务,如果返回null 循环结束

超时退出的机制只要超过时间获取不到任务 结束循环就行了,所以具体实现都是在getTask里的

里面有个很重要的内容就是 是否需要回收核心线程,默认不会收,可以通过方法设置

workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
使用超时获取的方式,如果超过时间无法获取到任务,那么 timedOut = true;

timedOut = true return null;

    private Runnable getTask() {
        boolean timedOut = false; // 默认没有超时

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // 如果核心线程也允许回收,那么全部进行回收,否则只有线程数大于核心数的时候进行回收

            if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { // 如果线程数量大于最大线程数量或者超时了 进行回收
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); // 如果需要回收 使用超时获取方式 否则使用 无限等待获取方式
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

3.2. 向线程池提交任务

可以使用两个方法向线程池提交任务,分别为 execute() 和 submit() 方法。

execute() 方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功。通过以下代码可知 execute() 方法输入的任务是一个 Runnable 类的实例。

threadsPool.execute(new Runnable() {
                        @Override
                        public void run() {
                                // TODO Auto-generated method stub
                        }
                });

submit() 方法用于提交需要返回值的任务。线程池会返回一个 future 类型的对象,通过这个 future 对象可以判断任务是否执行成功,并且可以通过 future 的 get() 方法来获取返回值,get() 方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。

Future<Object> future = executor.submit(harReturnValuetask);
                try {
                        Object s = future.get();
                } catch (InterruptedException e) {
                        // 处理中断异常
                } catch (ExecutionException e) {
                        // 处理无法执行任务异常
                } finally {
                        // 关闭线程池
                        executor.shutdown();
                }

3.3. 关闭线程池

可以通过调用线程池的 shutdown 或 shutdownNow 方法来关闭线程池

它们的原理是遍历线程池中的工作线程,然后逐个调用线程的 interrupt 方法来中断线程,所以无法响应中断的任务可能永远无法终止

只要调用了这两个关闭方法中的任意一个,isShutdown 方法就会返回 true

当所有的任务都已关闭后,才表示线程池关闭成功,这时调用 isTerminaed 方法会返回 true

shutdownNow与shutdown的区别

public void shutdown() {                                        public List<Runnable> shutdownNow() {
                                                                    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;                   final ReentrantLock mainLock = this.mainLock;                     
    mainLock.lock();                                                mainLock.lock();                                                 
    try {                                                           try {                                       
        checkShutdownAccess();                                          checkShutdownAccess();
        advanceRunState(SHUTDOWN);                                      advanceRunState(STOP);
        interruptIdleWorkers();                                         interruptWorkers();
        onShutdown();                                                   tasks = drainQueue();
    } finally {                                                     } finally {
        mainLock.unlock();                                              mainLock.unlock();
    }                                                               }
    tryTerminate();                                                 tryTerminate();
                                                                    return tasks;
}                                                               }        

shutdownNow 将线程池的状态设置成 STOP 状态 停止所有的正在执行或暂停任务的线程
shutdown 将线程池的状态设置成 SHUTDOWN 状态 中断所有没有正在执行任务的线程

4. 线程池状态

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    
    // 高三位 为 状态  其余位为线程数量  为什么这么些 是因为不同系统下 integer 的位数不同
    private static final int COUNT_BITS = Integer.SIZE - 3;
    
    // 线程最大个数 
    // 1 << COUNT_BITS      100000000000000000 
    // 1 << COUNT_BITS -1   100000000000000000-1 = 11111111111111111
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
    
    // 111 000000000000
    private static final int RUNNING    = -1 << COUNT_BITS;
    
    // 000 000000000000
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    
    // 001 000000000000
    private static final int STOP       =  1 << COUNT_BITS;
    
    // 010 000000000000
    private static final int TIDYING    =  2 << COUNT_BITS;
    
    // 011 000000000000
    private static final int TERMINATED =  3 << COUNT_BITS;

    // 获取高三位 后面带着0
    //xxx  0000000000000
    private static int runStateOf(int c)     { 
        return c & ~CAPACITY; 
    }
    
    // 获取低29位 
    private static int workerCountOf(int c)  { 
        return c & CAPACITY; 
    }
    
    // 计算ctl新值, 将状态码和数量拼起来
    private static int ctlOf(int rs, int wc) { 
        return rs | wc; 
    }

5. ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor。它主要用来在给定的延迟之后运行任务,或者定期执行任务

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
    }

    public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory);
    }

    public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), handler);
    }
    public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory, handler);
    }

我们从代码可以看出,ScheduledThreadPoolExecutor 的实现重点是 DelayedWorkQueue 队列,DelayedWorkQueue 是一个无界队列,所以 ThreadPoolExecutor 的 maximumPoolSize 在 ScheduledThreadPoolExecutor 中没有什么意义

5.1. 任务提交方法

    public void execute(Runnable command) {
        schedule(command, 0, NANOSECONDS);
    }

    public Future<?> submit(Runnable task) {
        return schedule(task, 0, NANOSECONDS);
    }
    
    public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
        if (command == null || unit == null) {
            throw new NullPointerException();
        }
        RunnableScheduledFuture<?> t = decorateTask(command, new ScheduledFutureTask<Void>(command, null, triggerTime(delay, unit)));
        delayedExecute(t);
        return t;
    }

    public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
        if (callable == null || unit == null) {
            throw new NullPointerException();
        }
        RunnableScheduledFuture<V> t = decorateTask(callable, new ScheduledFutureTask<V>(callable, triggerTime(delay, unit)));
        delayedExecute(t);
        return t;
    }

    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
        if (command == null || unit == null) {
            throw new NullPointerException();
        }
        if (period <= 0) {
            throw new IllegalArgumentException();
        }
        ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), unit.toNanos(period));
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);
        return t;
    }

    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
        if (command == null || unit == null) {
            throw new NullPointerException();
        }
        if (delay <= 0) {
            throw new IllegalArgumentException();
        }
        ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), unit.toNanos(-delay));
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);
        return t;
    }

参数:

  • Runnable command 要执行的任务
  • Callable callable 回掉函数
  • long delay 延迟时间
  • TimeUnit unit 时间单位
  • long initialDelay 初始化延迟时间
  • long period 执行周期
    scheduleAtFixedRatescheduleWithFixedDelay的区别
    scheduleAtFixedRate 定时周期执行 不管任务执行完成是否,按照时间点启动任务,但是任务如果没有执行完,下个任务也不会执行。
    scheduleWithFixedDelay 固定延迟执行 任务执行完成之后等待delay时间后再次执行

6. Executors 工厂类的使用

6.1 ThreadPoolExecutor

6.1.1. CachedThreadPool

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
    }
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory);
    }

大小无界的线程池,适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器,没有核心池,同时可能会有无数个线程在执行

  • 没有核心线程,所有的线程都会被回收
  • 回收的超时时间是60s
  • 只要有任务就会创建一个线程去执行
  • 队列里不会有任务存储,提交之后就会创建线程去处理

6.1.2. FixedThreadPool

    public static ExecutorService newFixedThreadPool(int nThreads){
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory){
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory);
    }

使用固定线程数的线程池,最多会有nThreads个任务执行

  • 核心线程数和总线程数相同
  • 线程不会被回收
  • 同时执行的线程数量只能 <= nThreads
  • 任务队列是无界队列,如果线程满了就会存储到队列,直到超过Integer.MAX_VALUE

6.1.3. SingleThreadExecutor

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory));
    }

创建使用单个线程的线程池,提交的各个任务会顺序的执行,并且永远不会有两个任务同时执行

  • 线程数量只有1个
  • 线程不会被回收
  • 同时执行的线程数量最多只能有1个
  • 来不及执行的任务都会存储到队列中,直到超过Integer.MAX_VALUE

6.2. ScheduledThreadPoolExecutor

定时调度线程池执行器

6.2.1. ScheduledThreadPool

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }

6.2.2 SingleThreadScheduledExecutor

    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));
    }
    
    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
        return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1, threadFactory));
    }

6.3. ForkJoinPool

6.3.1. ForkJoinPool的简单使用

ForkJoinPool使用时需要构建自己特有的任务类ForkJoinTask,而不是使用Runnable接口

ForkJoinTask不是Runnable的子类
也可以传入Runnable接口实现类任务 但是会被封装成 ForkJoinTask

public class ForkJoinPoolTest {
    static int[] nums = new int[1000000];
	static final int MAX_NUM = 50000;
	static Random r = new Random();
	
	static {
		for(int i=0; i<nums.length; i++) {
			nums[i] = r.nextInt(100);
		}
		
		System.out.println("---" + Arrays.stream(nums).sum()); //stream api
	}
	
	static class AddTaskRet extends RecursiveTask<Long> {
		
		private static final long serialVersionUID = 1L;
		int start, end;
		
		AddTaskRet(int s, int e) {
			start = s;
			end = e;
		}

		@Override
		protected Long compute() {
			
			if(end-start <= MAX_NUM) {
				long sum = 0L;
				for(int i=start; i<end; i++) sum += nums[i];
				return sum;
			} 
			
			int middle = start + (end-start)/2;
			
			AddTaskRet subTask1 = new AddTaskRet(start, middle);
			AddTaskRet subTask2 = new AddTaskRet(middle, end);
			subTask1.fork();
			subTask2.fork();
			
			return subTask1.join() + subTask2.join();
		}
		
	}
	
	public static void main(String[] args) throws IOException {

		ForkJoinPoolTest temp = new ForkJoinPoolTest();

		ForkJoinPool fjp = new ForkJoinPool();
		AddTaskRet task = new AddTaskRet(0, nums.length);
		fjp.execute(task);
		long result = task.join();
		System.out.println(result);
		
	}
}	

从上面的代码看,forkjoin池最大的特点就是fork,也就是会不断将任务拆分成子任务,子任务的子任务也就是分而治之的思想
第二个重点就是join,每个子任务计算完成之后将结果合并给父任务

6.3.2. ForkJoinTask分析

6.3.2.1. 类图结构及工作流程

image

  • RecursiveTask 有返回值的递归调用任务 抽象类
  • RecursiveAction 无返回值的递归调用任务 抽象类
  • ForkJoinTask内部类
    • AdaptedCallable Callable的ForkJoinTask适配器
    • AdaptedRunnable Runnable的ForkJoinTask适配器 有返回结果,但需要自己传入
    • AdaptedRunnableAction Runnable的ForkJoinTask适配器 无返回结果
    • RunnableExecuteAction Runnable的ForkJoinTask适配器 无返回结果,且有异常需要处理

image

6.3.2.2. 创建线程池
 private ForkJoinPool(int parallelism, ForkJoinWorkerThreadFactory factory, UncaughtExceptionHandler handler, int mode, String workerNamePrefix) {
        this.workerNamePrefix = workerNamePrefix;
        this.factory = factory;
        this.ueh = handler;
        this.config = (parallelism & SMASK) | mode;
        long np = (long)(-parallelism); // offset ctl counts
        this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
    }
  • int parallelism 并行数量 默认是cpu核心数,ForkJoinPool里线程数量依据于它,但不表示最大线程数,不等同于ThreadPoolExecutor里的corePoolSize或者maximumPoolSize
  • ForkJoinWorkerThreadFactory factory 线程工厂类
  • UncaughtExceptionHandler handler 未捕获异常处理器,当执行的任务中出现异常,并从任务中被抛出时,就需要用handler处理
  • int mode 模式 LIFO_QUEUE/FIFO_QUEUE/SHARED_QUEUE
  • String workerNamePrefix 线程名称前缀
6.3.2.3. 任务队列

WorkQueue是一个双端队列,它定义在ForkJoinPool类里。

scanState描述WorkQueue当前状态:

  • 偶数表示RUNNING
  • 奇数表示SCANNING
  • 负数表示inactive

操作WorkQueue前需要锁定,记录在字段qlock:

  • 1:锁定;
  • 0:未锁定;
  • 负数:对应的worker已经撤销注册,WorkQueue也就终止使用

WorkQueue也有config,不要和ForkJoinPool的config混淆了。WorkQueue的config记录了在WorkQueue[]的下标和当前mode

6.3.2.4. 提交任务

不管是submit还是execute 最后调用的都是externalPush,这种任务我们成为提交任务
还有一种是fork出来的子任务,两种任务都会存放在 WorkQueue 数组中

线程池里的线程也不再是Thread,而是Thread的子类 ForkJoinWorkerThread

6.3.2.5. 执行任务
  1. ForkJoinPool.createWorker -> wt.start(); 创建worker 启动线程
  2. ForkJoinWorkerThread.run() 执行线程额业务逻辑
  3. pool.runWorker(workQueue); 使用ForkJoinPool执行ForkJoinWorkerThread队列中的任务
    final void runWorker(WorkQueue w) {
        w.growArray();                   // allocate queue
        int seed = w.hint;               // initially holds randomization hint
        int r = (seed == 0) ? 1 : seed;  // avoid 0 for xorShift
        for (ForkJoinTask<?> t;;) {  // 循环获取任务
            if ((t = scan(w, r)) != null)
                w.runTask(t); // 执行任务
            else if (!awaitWork(w, r))
                break;
            r ^= r << 13; 
            r ^= r >>> 17; 
            r ^= r << 5; // xorshift
        }
    }

循环获取任务,然后使用WorkQueue去执行自己的任务,如果已经扫描不到任务,那么该线程会在awaitWork中park
4. WorkQueue.runTask(ForkJoinTask task)

    final void runTask(ForkJoinTask<?> task) {
        if (task != null) {
            scanState &= ~SCANNING; // mark as busy
            (currentSteal = task).doExec(); // 执行任务逻辑
            U.putOrderedObject(this, QCURRENTSTEAL, null); // release for GC
            execLocalTasks(); // 执行本地任务
            ForkJoinWorkerThread thread = owner;
            if (++nsteals < 0)      // collect on overflow
                transferStealCount(pool);
            scanState |= SCANNING;
            if (thread != null)
                thread.afterTopLevelExec();
        }
    }
  1. (currentSteal = task).doExec(); 执行任务类
    final int doExec() {
        int s; boolean completed;
        if ((s = status) >= 0) {
            try {
                completed = exec(); // 执行并得到是否完成
            } catch (Throwable rex) {
                return setExceptionalCompletion(rex);
            }
            if (completed)
                s = setCompletion(NORMAL);
        }
        return s;
    }

6.3.2.6. 任务偷取
   private ForkJoinTask<?> scan(WorkQueue w, int r) {
        WorkQueue[] ws; int m;
        if ((ws = workQueues) != null && (m = ws.length - 1) > 0 && w != null) {
            int ss = w.scanState;                     // initially non-negative
            for (int origin = r & m, k = origin, oldSum = 0, checkSum = 0;;) {
                WorkQueue q; 
                ForkJoinTask<?>[] a;
                ForkJoinTask<?> t;
                int b, n; 
                long c;
                if ((q = ws[k]) != null) {
                    if ((n = (b = q.base) - q.top) < 0 && (a = q.array) != null) {      // non-empty
                        long i = (((a.length - 1) & b) << ASHIFT) + ABASE;
                        if ((t = ((ForkJoinTask<?>) U.getObjectVolatile(a, i))) != null && q.base == b) {
                            if (ss >= 0) {
                                if (U.compareAndSwapObject(a, i, t, null)) {
                                    q.base = b + 1;
                                    if (n < -1) {      // signal others
                                        signalWork(ws, q);
                                    }
                                    return t;
                                }
                            }  else if (oldSum == 0 && w.scanState < 0) {
                                tryRelease(c = ctl, ws[m & (int)c], AC_UNIT);
                            }
                        }
                        if (ss < 0) // 如果状态小于0                    // refresh
                            ss = w.scanState;
                        r ^= r << 1; r ^= r >>> 3; r ^= r << 10;
                        origin = k = r & m;           // move and rescan
                        oldSum = checkSum = 0;
                        continue;
                    }
                    checkSum += b;
                }
                if ((k = (k + 1) & m) == origin) {    // continue until stable
                    if ((ss >= 0 || (ss == (ss = w.scanState))) &&
                        oldSum == (oldSum = checkSum)) {
                        if (ss < 0 || w.qlock < 0)    // already inactive
                            break;
                        int ns = ss | INACTIVE;       // try to inactivate
                        long nc = ((SP_MASK & ns) |
                                   (UC_MASK & ((c = ctl) - AC_UNIT)));
                        w.stackPred = (int)c;         // hold prev stack top
                        U.putInt(w, QSCANSTATE, ns);
                        if (U.compareAndSwapLong(this, CTL, c, nc))
                            ss = ns;
                        else
                            w.scanState = ss;         // back out
                    }
                    checkSum = 0;
                }
            }
        }
        return null;
    }

该方法会优先获取本身队列里的任务去执行,如果队列取空之后就会尝试去其他人的队列里偷取任务

  1. 通过计算队列数组索引k 得到一个WorkQueue 队列 q = ws[k] //这儿就有可能获取到别人的队列,所以是可能是偷取
  2. 如果队列的base小于top 说明该队列中还有没处理完的任务,那么就尝试从这里获取任务
  3. 如果取出了之后,目标队列仍旧有任务剩余,那么将会调用signalWorker(ws, q)方法尝试重新添加一个工作线程或者唤醒一个工作线程
  4. 如果在取得到任务后,该线程的scanState如果小于0也就是处于不活跃状态,那么将会通过tryRelease(c = ctl, ws[m & (int)c], AC_UNIT);重新唤起一个线程(使用unpark)
  5. 如果无法获取到任务,也就是队列被取空之后就会重新计算一个队列数组索引k 然后去取任务。
6.3.2.7. fork 构建子任务
    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. 如果当前线程是ForkJoinWorkerThread类型的,找到它的workQueue 工作队列 并将自己push进去
  2. 如果不是workQueue 调用makeCommonPool 创建一个ForkJoinPool来执行
6.3.2.8. join
    public final V join() {
        int s;
        if ((s = doJoin() & DONE_MASK) != NORMAL)
            reportException(s);
        return getRawResult();
    }
    private int doJoin() {
        int s; Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue w;
        return (s = status) < 0 ? s :
            ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
            (w = (wt = (ForkJoinWorkerThread)t).workQueue).
            tryUnpush(this) && (s = doExec()) < 0 ? s :
            wt.pool.awaitJoin(w, this, 0L) :
            externalAwaitDone();
    }
    
    

任务需要join的时候 如果子任务还在执行,那么会调用wt.pool.awaitJoin(w, this, 0L)进行等待阻塞,然后一层层的往上级任务jion

6.3.2.9. 异常处理

将异常处理器注册到Thread中,使用Thread的UncaughtExceptionHandler进行处理

 if ((handler = ueh) != null)
            wt.setUncaughtExceptionHandler(handler);

在执行过程中抛出的异常都会被捕获并存储到弱引用队列中,然后在join的时候来报告抛出异常
然后就会被UncaughtExceptionHandler捕获并处理

    final int doExec() {
        int s; boolean completed;
        if ((s = status) >= 0) {
            try {
                completed = exec();
            } catch (Throwable rex) {
                return setExceptionalCompletion(rex);
            }
            if (completed)
                s = setCompletion(NORMAL);
        }
        return s;
    }
    public final V join() {
        int s;
        if ((s = doJoin() & DONE_MASK) != NORMAL)
            reportException(s);
        return getRawResult();
    }
    private void reportException(int s) {
        if (s == CANCELLED)
            throw new CancellationException();
        if (s == EXCEPTIONAL)
            rethrow(getThrowableException());
    }
    static void rethrow(Throwable ex) {
        if (ex != null)
            ForkJoinTask.<RuntimeException>uncheckedThrow(ex);
    }
    static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
        throw (T)t; // rely on vacuous cast
    }

6.4. WorkStealingPool

    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool(Runtime.getRuntime().availableProcessors(),  ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
    }
    
    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool(parallelism, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
    }

WorkStealingPool 任务偷取池就是使用ForkJoinPool来实现并给一些默认参数

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值