本文转自:生产者消费者模式之工作窃取算法
生产者消费者模式之工作窃取算法
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 方法从队列中取元素,当队列中没有元素时,不会阻塞)。