生产者消费者模式之工作窃取算法

本文转自:生产者消费者模式之工作窃取算法

生产者消费者模式之工作窃取算法

1、一个通道只有一个队列,多个消费者共享一个队列实例,导致锁的竞争,如果一个通道拥有多个队列,则消费者可以从通道中获取各自队列获取数据。

2、如要服务有高性能和可靠性的要求,Consumer-Producer模式请使用 kafka等开源工具

通道相关接口

public interface Channel<P> {

    public P take() throws InterruptedException;

    public void put(P product) throws InterruptedException;

}

public interface WorkStealingEnableChannel<P> extends Channel<P> {

    P take(BlockingDeque<P> preferredQueue) throws InterruptedException;

}

工作窃取算法实现

import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;

public class WorkStealingChannel<P> implements WorkStealingEnableChannel<P> {

    // 双端队列,可以从两端插入值或获取值,继承了BlockingQueue
    private final BlockingDeque<P>[] managedQueues;

    public WorkStealingChannel(BlockingDeque<P>[] managedQueues) {
        super();
        this.managedQueues = managedQueues;
    }

    @Override
    public P take() throws InterruptedException {
        return take(null);
    }

    @Override
    public void put(P product) throws InterruptedException {
        int targetIndex = (product.hashCode() % managedQueues.length);
        BlockingQueue<P> targetQueue = managedQueues[targetIndex];
        targetQueue.put(product);
    }   

    @Override
    public P take(BlockingDeque<P> preferredQueue) throws InterruptedException {
        BlockingDeque<P> targetQueue = preferredQueue;
        P product = null;

        int targetIndex = 0;
        // 优先从指定的队列获取值
        if(null != targetQueue) {
            product = targetQueue.poll();
            for (int i=0; i<managedQueues.length; i++) {
                if(targetQueue == managedQueues[i] ) {
                    targetIndex = i;
                    break;
                }
            }
            if(product != null) {
                System.out.println(targetIndex + " processing " + product);
            }
        }

        int queueIndex = -1;            
        if(null == product) {
            //随机窃取 其他受管队列的产品
            queueIndex = (int) (System.currentTimeMillis() % managedQueues.length);
            targetQueue = managedQueues[queueIndex];
            product = targetQueue.pollLast();
            if(product != null && preferredQueue != targetQueue) {
                System.out.println(targetIndex + " stealed from " + queueIndex + ": " + product);
            } else if(product != null) {
                System.out.println(targetIndex + " processing " + product);
            }
        }

        return product;
    }

}

示例

import java.util.Random;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;

public class WorkStealingExample {

    private final WorkStealingEnableChannel<String> channel;
    private final BlockingDeque<String>[] managedQueues;
    private final int nCPU;
    private final int consumerCount;
    private Consumer[] consumers;
    private Producer[] producers;
    private volatile AtomicInteger count = new AtomicInteger();

    @SuppressWarnings("unchecked")
    public WorkStealingExample() {
        this.nCPU = Runtime.getRuntime().availableProcessors();
        this.consumerCount = this.nCPU/2 + 1;
        System.out.println(nCPU + ", " + consumerCount);

        managedQueues = new LinkedBlockingDeque[consumerCount];
        channel = new WorkStealingChannel<String>(managedQueues);

        consumers = new Consumer[consumerCount];
        for(int i=0; i<consumerCount; i++){
            managedQueues[i] = new LinkedBlockingDeque<String>();
            consumers[i] = new Consumer(managedQueues[i]);
        }

        producers = new Producer[nCPU];
        for(int i=0; i<nCPU; i++){
            producers[i] = new Producer();
        }
    }

    private class Producer extends Thread {
        private boolean stopFlag = false;

        @Override
        public void run() {
            String value;
            while(!stopFlag) {
                try {
                    value = String.valueOf(count.incrementAndGet());
                    channel.put(value);

                    System.out.println(Thread.currentThread().getName() + " produce product: " + value);

                    Thread.sleep(new Random().nextInt(85));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public void stopWork() {
            this.stopFlag = true;
        }
    }

    private class Consumer extends Thread {
        private volatile boolean stopFlag = false;
        private final BlockingDeque<String> workQueue; //消费者默认取数据的队列

        public Consumer(BlockingDeque<String> workQueue) {
            this.workQueue = workQueue;
        }

        @Override
        public void run() {
             /**
             * 实现了工作窃取算法
             */
            while(!stopFlag) {
                try {
                    channel.take(workQueue);

                    Thread.sleep(new Random().nextInt(50));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public void stopWork() {
            this.stopFlag = true;
        }
    }

    public void startAll() {
        for(int i=0; i<consumers.length; i++){
            consumers[i].start();
        }

        for(int i=0; i<producers.length; i++){
            producers[i].start();
        }
    }

    public void stopAll() {
        for(int i=0; i<producers.length; i++){
            producers[i].stopWork();
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for(int i=0; i<consumers.length; i++){
            consumers[i].stopWork();
        }
    }

    public static void main(String[] args) {
        WorkStealingExample example = new WorkStealingExample();
        example.startAll();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        example.stopAll();
    }

}

运行结果如下:

4, 3
Thread-3 produce product: 2
Thread-5 produce product: 1
Thread-4 produce product: 3
Thread-6 produce product: 4
Thread-6 produce product: 5
1 processing 1
Thread-4 produce product: 6
0 processing 3
2 processing 2
0 processing 6
Thread-4 produce product: 7
0 stealed from 2: 5
Thread-5 produce product: 8
1 processing 4
Thread-6 produce product: 9
Thread-3 produce product: 10
0 processing 9
2 processing 8
2 stealed from 1: 10
Thread-6 produce product: 11
1 processing 7
Thread-5 produce product: 12
Thread-5 produce product: 13
0 processing 12
Thread-6 produce product: 14
Thread-4 produce product: 15
2 processing 11
0 processing 15
2 processing 14
1 processing 13
Thread-4 produce product: 16
Thread-3 produce product: 17
2 processing 17
Thread-3 produce product: 18
1 processing 16
0 processing 18
Thread-6 produce product: 19
Thread-5 produce product: 20
0 stealed from 1: 19
Thread-6 produce product: 21
1 stealed from 2: 20
0 processing 21
Thread-6 produce product: 22
Thread-6 produce product: 23
2 processing 23
Thread-3 produce product: 24
Thread-4 produce product: 25
1 processing 22
2 stealed from 0: 24
1 processing 25
Thread-4 produce product: 26
Thread-5 produce product: 27
Thread-3 produce product: 28
0 processing 27
Thread-6 produce product: 29
1 processing 28
2 processing 26
Thread-6 produce product: 30
Thread-3 produce product: 31
Thread-6 produce product: 32
0 processing 30
Thread-4 produce product: 33
2 processing 29
1 processing 31
0 processing 33
Thread-5 produce product: 34
2 processing 32
1 processing 34
Thread-3 produce product: 35
Thread-3 produce product: 36
Thread-4 produce product: 37
Thread-6 produce product: 38
0 processing 36
Thread-5 produce product: 39
Thread-6 produce product: 40
1 processing 37
2 processing 35
Thread-4 produce product: 41
0 processing 39
1 processing 40
2 processing 38
Thread-5 produce product: 42
0 processing 42
Thread-3 produce product: 43
Thread-5 produce product: 44
1 processing 43
2 processing 41
0 stealed from 2: 44
Thread-3 produce product: 45
Thread-6 produce product: 46
Thread-3 produce product: 47
0 processing 45
Thread-4 produce product: 48
1 processing 46
2 processing 47
Thread-5 produce product: 49
Thread-5 produce product: 50
0 processing 48
2 processing 50
1 processing 49
Thread-5 produce product: 51
Thread-5 produce product: 52
Thread-6 produce product: 54
Thread-3 produce product: 53
Thread-3 produce product: 55
Thread-4 produce product: 56
0 processing 51
2 processing 53
Thread-6 produce product: 57
Thread-5 produce product: 58
1 processing 52
1 processing 55
Thread-4 produce product: 59
Thread-5 produce product: 60
Thread-5 produce product: 61
1 processing 58
0 processing 54
Thread-5 produce product: 62
1 processing 61
2 processing 56
0 processing 57
1 stealed from 2: 62
Thread-6 produce product: 63
Thread-3 produce product: 64
Thread-4 produce product: 65
2 processing 59
Thread-4 produce product: 66
0 processing 60
Thread-6 produce product: 67
Thread-5 produce product: 68
Thread-5 produce product: 69
1 processing 64
2 processing 65
1 processing 67
0 processing 63
Thread-3 produce product: 70
2 processing 68
Thread-4 produce product: 71
Thread-3 produce product: 72
1 processing 70
Thread-5 produce product: 73
0 processing 66
Thread-6 produce product: 74
2 processing 71
Thread-5 produce product: 75
Thread-4 produce product: 76
Thread-5 produce product: 77
1 processing 73
0 processing 69
Thread-5 produce product: 78
Thread-4 produce product: 79
2 processing 74
0 processing 72
Thread-6 produce product: 80
Thread-3 produce product: 81
1 processing 76
0 processing 75
Thread-4 produce product: 82
Thread-5 produce product: 83
2 processing 77
0 processing 78
Thread-6 produce product: 84
Thread-6 produce product: 85
2 processing 80
0 processing 81
1 processing 79
0 processing 84
Thread-4 produce product: 86
2 processing 83
Thread-3 produce product: 87
1 processing 82
2 processing 86
Thread-3 produce product: 88
2 stealed from 0: 87
Thread-6 produce product: 89
1 processing 85
Thread-5 produce product: 90
0 processing 90
1 processing 88
Thread-5 produce product: 91
1 processing 91
0 stealed from 2: 89
Thread-4 produce product: 92
Thread-3 produce product: 93
2 processing 92
Thread-6 produce product: 94
1 processing 94
0 processing 93
Thread-6 produce product: 95
Thread-5 produce product: 96
2 processing 95
0 processing 96
Thread-5 produce product: 97
Thread-3 produce product: 98
Thread-5 produce product: 99
Thread-4 produce product: 100
Thread-6 produce product: 102
Thread-4 produce product: 101
1 processing 97
0 processing 99
Thread-3 produce product: 103
2 processing 98
1 processing 100
1 processing 103
0 processing 102
Thread-5 produce product: 104
2 processing 101
Thread-4 produce product: 105
0 processing 105
1 stealed from 2: 104

从上面的运行结果可以看出,消费者在自己的队列中没有元素时,会尝试随机从其它队列的末尾取元素(由于使用的 poll 方法从队列中取元素,当队列中没有元素时,不会阻塞)。

猜你喜欢

转载自blog.csdn.net/xxc1605629895/article/details/81089819