ForkJoin框架详解 一张图搞明白工作窃取(work-stealing)机制

本文深入剖析ForkJoin框架的工作原理,包括ForkJoinPool的内部结构、工作窃取机制、任务提交、运行及结果获取流程。通过示例代码演示线程间的任务窃取过程,帮助读者理解ForkJoin框架在并发编程中的应用。

1 ForkJoin框架 

1.1 ForkJoin框架

ForkJoinPool一种ExecutorService的实现,运行ForkJoinTask任务。ForkJoinPool区别于其它ExecutorService,主要是因为它采用了一种工作窃取(work-stealing)的机制。所有被ForkJoinPool管理的线程尝试窃取提交到池子里的任务来执行,执行中又可产生子任务提交到池子中。

    ForkJoinPool维护了一个WorkQueue的数组(数组长度是2的整数次方,自动增长)。每个workQueue都有任务队列(ForkJoinTask的数组),并且用base、top指向任务队列队尾和队头。work-stealing机制就是工作线程挨个扫描任务队列,如果队列不为空则取队尾的任务并执行。示意图如下:

1.2 demo小程序

    创建一个包含2个worker线程的pool,main线程提交2个任务(task-1、task-2),触发worker线程工作,task-1任务fork出4个子任务。main线程负责同步两个worker线程的工作进度。demo小程序演示了worker-2窃取worker-1的子任务:

package com.focuse.jdkdemo.concurrent;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;

/**
 * @date :Created in 2020/2/16 上午11:10
 * @description:
 * @modified By:
 */
public class ForkJoinDemo {
    private static Thread worker1 = null;
    private static Thread worker2 = null;
    private static AtomicBoolean  worker1Park = new AtomicBoolean();
    private static AtomicBoolean  worker2Park = new AtomicBoolean();
    static {
        worker1Park.set(false);
        worker2Park.set(false);
    }

    public abstract static class ForkJoinTaskDemo extends ForkJoinTask {
        @Override
        public Object getRawResult() {
            return null;
        }

        @Override
        protected void setRawResult(Object value) {

        }
    }

    public static void setParkFlag() {
        if (Thread.currentThread().getName().equals("worker-1")) {
            //自旋设置, 保证与main线程同步
            while (!worker1Park.compareAndSet(false, true)) {

            }
        } else if (Thread.currentThread().getName().equals("worker-2")) {
            //自旋设置, 保证与main线程同步
            while (!worker2Park.compareAndSet(false, true)) {

            }
        }
    }

    private static Runnable task1 = new Runnable() {
        @Override
        public void run() {
            worker1 = Thread.currentThread();
            worker1.setName("worker-1");
            System.out.println(Thread.currentThread().getName() + " execute task1");
            //暂停并且设置暂停flag以便main结束自旋
            setParkFlag();
            LockSupport.park();
            ForkJoinTask task11 = new ForkJoinTaskDemo() {
                @Override
                protected boolean exec() {
                    System.out.println(Thread.currentThread().getName() + " execute task1-1");
                    //暂停并且设置暂停flag以便main结束自旋
                    setParkFlag();
                    LockSupport.park();
                    return true;
                }
            };
            task11.fork();
            ForkJoinTask task12 = new ForkJoinTaskDemo() {
                @Override
                protected boolean exec() {
                    System.out.println(Thread.currentThread().getName() + " execute task1-2");
                    //暂停并且设置暂停flag以便main结束自旋
                    setParkFlag();
                    LockSupport.park();
                    return true;
                }
            };
            task12.fork();
            ForkJoinTask task13 = new ForkJoinTaskDemo() {
                @Override
                protected boolean exec() {
                    System.out.println(Thread.currentThread().getName() + " execute task1-3");
                    //暂停并且设置暂停flag以便main结束自旋
                    setParkFlag();
                    LockSupport.park();
                    return true;
                }
            };
            task13.fork();
            ForkJoinTask task14 = new ForkJoinTaskDemo() {
                @Override
                protected boolean exec() {
                    System.out.println(Thread.currentThread().getName() + " execute task1-4");
                    //暂停并且设置暂停flag以便main结束自旋
                    setParkFlag();
                    LockSupport.park();
                    return true;
                }
            };
            task14.fork();
            //提交4个子任务后暂停
            setParkFlag();
            LockSupport.park();
        }
    };

    private static Runnable task2 = new Runnable() {
        @Override
        public void run() {
            worker2 = Thread.currentThread();
            worker2.setName("worker-2");
            System.out.println(Thread.currentThread().getName() + " execute task2");
            //暂停并且设置暂停flag以便main结束自旋
            setParkFlag();
            LockSupport.park();
        }
    };

    public static void main(String[] args) {
        //只用两个线程,方便测试
        ForkJoinPool pool = new ForkJoinPool(2);
        //step-1、step-2 提交2各任务,期望worker-1 worker-2各执行一个任务
        System.out.println("step-1 step-2: 提交2个任务,触发2个worker线程");
        pool.submit(task1);
        pool.submit(task2);

        while (!worker1Park.get()) {
        }
        //step-3  唤醒worker-1 产生4个任务 然后worker-1继续暂停
        System.out.println("\n*******************************************");
        System.out.println("step-3 唤醒worker-1 产生4个子任务");
        LockSupport.unpark(worker1);
        //自旋设置, 保证与worker-1线程同步
        while (!worker1Park.compareAndSet(true, false)) {
        }


        while (!worker1Park.get() || !worker2Park.get()) {
        }
        //step-4 唤醒worker-1 worker-2
        System.out.println("\n*******************************************");
        System.out.println("step-4 唤醒worker-1、worker-2:worker-1弹出task1-4执行,worker-2窃取task1-1执行");
        LockSupport.unpark(worker1);
        LockSupport.unpark(worker2);
        //自旋设置, 保证与worker-1线程同步
        while (!worker1Park.compareAndSet(true, false)) {
        }
        //自旋设置, 保证与worker-2线程同步
        while (!worker2Park.compareAndSet(true, false)) {
        }

        while (!worker1Park.get() || !worker2Park.get()) {
        }
        //step-5 唤醒worker-1 worker-2
        System.out.println("\n*******************************************");
        System.out.println("step-5 唤醒worker-1、worker-2:worker-1弹出task1-3执行,worker-2窃取task1-2执行");
        LockSupport.unpark(worker1);
        LockSupport.unpark(worker2);
        //自旋设置, 保证与worker-1线程同步
        while (!worker1Park.compareAndSet(true, false)) {
        }
        //自旋设置, 保证与worker-2线程同步
        while (!worker2Park.compareAndSet(true, false)) {
        }


        while (!worker1Park.get() || !worker2Park.get()) {
        }
        //唤醒worker-1 worker-2 结束
        LockSupport.unpark(worker1);
        LockSupport.unpark(worker
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值