[并发实现原理] 0703 ForkJoinPool 工作窃取队列

一、介绍

1、工作窃取算法,是指一个Worker线程在执行完毕自己队列中的任务之后,可以窃取其他线程队列中的任务来执行,从而实现负载均衡,以防有的线程很空闲,有的线程很忙。这个过程要用到工作窃取队列。

队列: 基于一个普通的数组实现

2、队列的三个操作

  1. Worker线程自己,在队列头部,通过对queueTop指针执行加、减操作,实现入队或出队,是单线程的。
  1. 其他Worker线程,在队列尾部,通过对queueBase进行累加,实现出队操作,窃取,是多线程的,需要通过CAS操作。
  1. queue top不是volatile的,queue base 是volatile类型。
        volatile int base;         // index of next slot for poll
        int top;                   // index of next slot for push
  1. 扩容源码

(1)扩容之后的新数组还是空的时候,array。而queueTop、queueBase的值是不变的,其他窃取线程若此时来窃取任务,取到的将全是null,即取不到任务。不过,虽然此时窃取不到,可以阻塞一会儿,待扩容完成就可以窃取到了,不会影响整个算法的正确性。

(2)在把旧数组的元素复制过来之前,先通过CAS操作把旧数组中的该元素置为null。只有CAS成功置为null了,才能赋值到新数组。这样可以避免同1个元素在旧数组、新数组中各有1份。1个窃取线程还在读旧数组,另1个窃取线程读取新数组,导致同1个元素被2个线程重复窃取。

        /**
         * Initializes or doubles the capacity of array. Call either
         * by owner or with lock held -- it is OK for base, but not
         * top, to move while resizings are in progress.
         */
        final ForkJoinTask<?>[] growArray() {
            ForkJoinTask<?>[] oldA = array;
            int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY;
            if (size > MAXIMUM_QUEUE_CAPACITY)
                throw new RejectedExecutionException("Queue capacity exceeded");
            int oldMask, t, b;
            ForkJoinTask<?>[] a = array = new ForkJoinTask<?>[size];
            if (oldA != null && (oldMask = oldA.length - 1) >= 0 &&
                (t = top) - (b = base) > 0) {
                int mask = size - 1;
                do { // emulate poll from old array, push to new array
                    ForkJoinTask<?> x;
                    int oldj = ((b & oldMask) << ASHIFT) + ABASE;
                    int j    = ((b &    mask) << ASHIFT) + ABASE;
                    x = (ForkJoinTask<?>)U.getObjectVolatile(oldA, oldj);
                    if (x != null &&
                        U.compareAndSwapObject(oldA, oldj, x, null))
                        U.putObjectVolatile(a, j, x);
                } while (++b != t);
            }
            return a;
        }
  1. poll 源码
        /**
         * Takes next task, if one exists, in FIFO order.
         */
        final ForkJoinTask<?> poll() {
            ForkJoinTask<?>[] a; int b; ForkJoinTask<?> t;
            while ((b = base) - top < 0 && (a = array) != null) {
                int j = (((a.length - 1) & b) << ASHIFT) + ABASE;
                t = (ForkJoinTask<?>)U.getObjectVolatile(a, j);
                if (base == b) {
                    if (t != null) {
                        if (U.compareAndSwapObject(a, j, t, null)) {
                            base = b + 1;
                            return t;
                        }
                    }
                    else if (b + 1 == top) // now empty
                        break;
                }
            }
            return null;
        }

3、整个队列是环形的,是一个数组实现的RingBuffer。

  1. 并且queueBase会一直累加,不会减小;queueTop会累加、减小。
  1. 当queueTop-queueBase=queue.length-1的时候,队列为满,扩容;
  1. 当queueTop=queueBase的时候,队列为空,Worker线程即将进入阻塞状态。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值