多线程设计范式-WorkerThread模式

文章目录

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();
            }
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值