1.What
WorkerThread可以理解为流水线模式, 上游的工人完成一个电子器件的组装之后(线程干完上一个任务), 将器件放在传送带(向缓冲中写入元素), 传送带的装配工接着装配, 再传递给下一个装配工.
线程池有点像流水线模式. A.submit(Runnable B)之后, A不再管线程池是怎么调度\怎么执行B了.
那跟生产者-消费者模式有啥区别呢?
看下图:
-
生产者-消费者模式:
Producer-Consumer是完全解耦的, Queue不需要感知到Consumer的存在; 虽然Producer-Consumer 都需要感知到Queue. -
WorkerThread模式:
为了方便讲解, 我们提出一个 channel的概念 ,可以类比为一个"流水线的传送带", producer将 任务放到 channel 中, channel是 内部缓冲Queue 和 Worker的聚合.channel必须知道 Worker的存在
那这种模式有啥实际应用呢?
个人觉得, 不如Producer-Consumer 模式来得容易实现易理解, WorkerThread模式像是将Queue 和Consumer 封装在了一起. 目前还不知道在什么框架或项目中实际应用到了…
2.How
举个场景:流水线工人在流水线传送带上根据产品说明书组装器件,上一零件装完后,交给下一个人装下一个零件.
类比:
一个线程从缓冲队列取任务并完成后,再交给下一个线程接着干.
public class WorkThreadTest {
// 测试: 上游几个人一直向流水线放待加工品.下游传送带正常加工
public static void main(String[] args) {
ProductionChannel channel = new ProductionChannel(6);
AtomicInteger prodIdGen = new AtomicInteger(1);
IntStream.range(1, 8).forEach(index -> {
new Thread() {
@Override
public void run() {
while (true) {
channel.offer(new Production(prodIdGen.getAndIncrement()));
try {
Thread.sleep(((int) (Math.random() * 2000)));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
});
}
}
// 举例: 流水线工人在传送带上根据说明书来组装产品.在 worker thread模式中, 流水线工人是传送带的一部分(注意理解这个)
// 说明书
abstract class InstructionBook {
void create() {
preprocess();
process();
}
abstract void preprocess();
abstract void process();
}
class Production extends InstructionBook {
private int prodId;
public Production(int prodId) {
this.prodId = prodId;
}
@Override
void preprocess() {
System.out.println(Thread.currentThread().getName() + " preprocess prodId:" + prodId);
}
@Override
void process() {
System.out.println(Thread.currentThread().getName() + " process prodId:" + prodId);
}
@Override
public String toString() {
return String.valueOf(prodId);
}
}
// 流水线的抽象
class ProductionChannel {
//传送带容量(定长的)
private final static int CAPACITY = 100;
// 存放待加工产品
private Production[] productions;
// 注意这里的头尾并不是指针来指向 productions 的头尾的; 而是用来实现一个传动带上的N个工位, 前面一个工位把活干了,后面一个工位才能干.
// 放和取都是从第一个工位向后面工位. offer 从第一个工位放, take 从第一个工位取
// 头
private int head;
// 尾
private int tail;
// 当前传送带产品数
private int total;
// 传送带工人
private Worker[] workers;
public ProductionChannel(int workerNum) {
workers = new Worker[workerNum];
productions = new Production[CAPACITY];
for (Worker worker : workers) {
worker = new Worker("worker", this);
worker.start();
}
}
// 向工位上挨个放待处理产品
synchronized void offer(Production production) {
while (total >= productions.length) {
try {
this.wait();
} catch (InterruptedException e) {
//ignore
}
}
// 代码跑到这里说明传送带上有空位了
productions[tail] = production;
tail = (tail + 1) % productions.length;
total++;
this.notifyAll();
}
// 从工位上挨个取待处理产品
synchronized Production take() {
while (total <= 0) {
try {
this.wait();
} catch (InterruptedException e) {
//ignore
}
}
// 代码跑到这里说明传送带有产品了
Production prod = productions[head];
head = (head + 1) % productions.length;
total--;
this.notifyAll();
return prod;
}
}
// 流水线工人
class Worker extends Thread {
private ProductionChannel productionChannel;
private static final AtomicInteger idGen = new AtomicInteger(0);
public Worker(String workerName, ProductionChannel productionChannel) {
super(workerName + idGen.getAndIncrement());
this.productionChannel = productionChannel;
}
@Override
public void run() {
while (true) {
Production production = productionChannel.take();
System.out.println(getName() + " process the " + production);
production.create();
try {
TimeUnit.MILLISECONDS.sleep(((int) (Math.random() * 1000)));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}