Worker是“工人”的意思,worker thread pattern中,工人线程(worker thread)会一次抓一件工作来处理,当没有工作可做时,工人线程会停下来等待心得工作过来。

Worker Thread也叫做background thread,另外,也有人把视点放在管理工人线程的地方,称之为Thread Pool。

最近闲时在看《Java多线程设计模式》,很不错,语言浅显易懂,而且编排也好,很有启发性,现在挑其中一章来写写心得

worker thread是我们平时用的很多的一种多线程模式,只不过我们常常不把它当模式罢了。基本内容是:有一个流水线(channel),流水线一端有客户线程client,另一端有工人线程worker,客户不断把新的任务(request)放入流水线,工人在另一头获得任务,并执行,客户和工人的数量可多可少,就这么简单

这个所谓的pattern初看好像似曾相识,就是一个thread pool嘛, 按通常的做法,request可以实现Runnable接口,把要做的事情放在run方法中,由worker去执行,具体实现时还要注意同步的问题

不过,由此我们可以想到Swing的工作方式。Swing是事件驱动的,它有一个the event-dispatch queue,这里之所以用the,是因为这个队列是唯一的,就和上面说的流水线一样。Swing的各个组件相当于客户,不断把各种事件(键盘或者鼠标事件,等等)塞入event queue中,queue有个专门的线程负责把这些事件送给相应的listener,就实现了最基本的事件驱动模型。如果不采用这种模型,即事件由专门线程处理的话,界面的相应速度就很差了

如果有用过Java做游戏的话,应该都接触过javax.swing.SwingUtilities这个类,里面有个invokeAndWait方法,就是用来让其他线程操作Swing组件的。为什么不能直接操作呢,如上所述,event dispatch queue是唯一的,因此Swing组件在设计时就没有过多考虑多线程的问题,反正由event dispatch queue统一操作,这样可以提高速度(尽管Swing本来就很慢),但是当其他线程要操作Swing组件时,就可能有潜在的不稳定因素,所以才有了invokeAndWait方法,调用此方法的线程会wait直到所需操作已经完成

还没说完,再仔细想想,我们在程序里也常常对那些组件直接操作而非用什么invokeAndWait,这里又有一些细节值得注意。首先是在你调用组件的setVisible等方法之前,你是可以随便改组件的,调用完setVisible之后,只有少数方法,比如repaint,addListener等等。最后,根据jdk文档所言,这个方法是用来给应用程序线程改变GUI外观的。如果非要直接改,不一定会出错,多线程本来就是比较难说的,呵呵,我也没试过,改天可以尝试一下。

每 個執行緒(线程)處理一個請求,每次執行緒執行完請求後,再次嘗試取得下一個請求並執行,這是Worker Thread的基本概念,對於一些需要冗長計算或要在背景執行的請求,可以採用Worker Thread。

在 Thread-Per-Message 模式中,其實已經有點Worker Thread的概念,在Service物件接收到資料後,以匿名方式建立執行緒來處理資料,那個建立的執行緒就是Worker Thread,只不用過就丟了。

Worker Thread可以應用在不同的場合,例如在 Guarded Suspension 模式 的範例,是使用一個執行緒來處理請求佇列中的請求,如果請求不斷來到,且請求中可能有冗長的處理,則請求佇列中的請求可能會來不及消化。

您可以為請求佇列中的每個請求配給一個執行緒來處理,不過實際上,只要建立足夠多的執行緒即可,

Worker Thread模式在Request的管理上像是 Producer Consumer 模式,在Request的行为上像是 Command 模式

Producer Consumer模式专注于Product的生产与消费,至于Product被消费时是作何处理,则不在它的讨论范围之中。

如果您的Product是一个Request,消费者取得Request之后,执行Request中指定的请求方法,也就是使用Command模式,并且您的Request缓冲区还管理了Consumer,就有Worker Thread模式的意思了。

在Sequence Diagram上,可以看出Worker Thread同时展现了Producer Consumer模式与Command模式:

public class Request {
private final String name; // 委托者
private final int number; // 请求编号
private static final Random random = new Random();
public Request(String name, int number) {
this.name = name;
this.number = number;
}
public void execute() {
System.out.println(Thread.currentThread().getName() + " executes " + this);
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
}
public String toString() {
return "[ Request from " + name + " No." + number + " ]";
}
}

ClientThread:

public class ClientThread extends Thread {
private final Channel channel;
private static final Random random = new Random();
public ClientThread(String name, Channel channel) {
super(name);
this.channel = channel;
}
public void run() {
try {
for (int i = 0; true; i++) { //可以修改为合适的
Request request = new Request(getName(), i);
channel.putRequest(request);
Thread.sleep(random.nextInt(1000));
}
} catch (InterruptedException e) {
}
}
}

WorkerThread:

public class WorkerThread extends Thread {
private final Channel channel;
public WorkerThread(String name, Channel channel) {
super(name);
this.channel = channel;
}
public void run() {
while (true) {
Request request = channel.takeRequest();
request.execute();
}
}
}

Channel: 可以LinkedList

public class Channel {
private static final int MAX_REQUEST = 100;
private final Request[] requestQueue;
private int tail; // 下一个putRequest的地方
private int head; // 下一个takeRequest的地方
private int count; // Request的数量 private final WorkerThread[] threadPool; public Channel(int threads) {
this.requestQueue = new Request[MAX_REQUEST];
this.head = 0;
this.tail = 0;
this.count = 0; threadPool = new WorkerThread[threads];
for (int i = 0; i < threadPool.length; i++) {
threadPool[i] = new WorkerThread("Worker-" + i, this);
}
}
public void startWorkers() {
for (int i = 0; i < threadPool.length; i++) {
threadPool[i].start();
}
}
public synchronized void putRequest(Request request) {
while (count >= requestQueue.length) {
try {
wait();
} catch (InterruptedException e) {
}
}
requestQueue[tail] = request;
tail = (tail + 1) % requestQueue.length;
count++;
notifyAll();
}
public synchronized Request takeRequest() {
while (count <= 0) {
try {
wait();
} catch (InterruptedException e) {
}
}
Request request = requestQueue[head];
head = (head + 1) % requestQueue.length;
count--;
notifyAll();
return request;
}
}

测试:

public class Main {
public static void main(String[] args) {
Channel channel = new Channel(5); // 工人线程的數量
channel.startWorkers();
new ClientThread("Alice", channel).start();
new ClientThread("Bobby", channel).start();
new ClientThread("Chris", channel).start();
}
}

参考了:http://www.riabook.cn/doc/designpattern/WorkerThread.htm

http://openhome.cc/Gossip/DesignPattern/WorkerThread.htm

http://chxiaowu.iteye.com/blog/1320767

更多:http://developer.51cto.com/art/201305/395279_1.htm

多线程 Worker Thread 模式的更多相关文章

  1. 多线程系列之九:Worker Thread模式

    一,Worker Thread模式 也叫ThreadPool(线程池模式) 二,示例程序 情景:一个工作车间有多个工人处理请求,客户可以向车间添加请求.请求类:Request定义了请求的信息和处理该请 ...

  2. Worker Thread模式

    工人线程Worker thread会逐个取回工作并进行处理,当所有工作全部完成后,工人线程会等待新的工作到来 5个工人线程从传送带取数据,3个传送工人线程将数据放入传送带 public class C ...

  3. 多线程程序设计学习(9)worker pattern模式

    Worker pattern[工作模式]一:Worker pattern的参与者--->Client(委托人线程)--->Channel(通道,里边有,存放请求的队列)--->Req ...

  4. 14.多线程设计模式 - Master-Worker模式

    多线程设计模式 - Master-Worker模式 并发设计模式属于设计优化的一部分,它对于一些常用的多线程结构的总结和抽象.与串行相比并行程序结构通常较为复杂,因此合理的使用并行模式在多线程并发中更 ...

  5. reactor模式:多线程的reactor模式

    上文说到单线程的reactor模式 reactor模式:单线程的reactor模式 单线程的reactor模式并没有解决IO和CPU处理速度不匹配问题,所以多线程的reactor模式引入线程池的概念, ...

  6. ios基础篇(二十九)—— 多线程(Thread、Cocoa operations和GCD)

    一.进程与线程 1.进程 进程是指在系统中正在运行的一个应用程序,每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内: 如果我们把CPU比作一个工厂,那么进程就好比工厂的车间,一个工厂有 ...

  7. php Pthread 多线程 Worker

    <?php //PHP 高级编程之多线程 http://www.netkiller.cn/journal/thread.php.html#idp57489856 //worker 是一个具有持久 ...

  8. 多线程设计模式 - Future模式

    Future模式是多线程开发中非常常见的一种设计模式,它的核心思想是异步调用.这类似我们日常生活中的在线购物流程,带在购物网看着一件商品时可以提交表单,当订单完成后就可以在家里等待商品送货上门.或者说 ...

  9. 13.多线程设计模式 - Future模式

    多线程设计模式 - Future模式 并发设计模式属于设计优化的一部分,它对于一些常用的多线程结构的总结和抽象.与串行相比并行程序结构通常较为复杂,因此合理的使用并行模式在多线程并发中更具有意义. 1 ...

随机推荐

  1. 饿了么 ---Java面试

    下午去饿了么参加面试,其实也满怀期待,毕竟也是个大公司. 交通:偏外环,真北路 环境:感觉压抑,不通风,面试人很多,可能是屋子高度低,不舒服. 填了资料,等待面试,两轮,真是憋屈 都是搞技术的,何苦为 ...

  2. EntityFramework 使用Linq处理内连接(inner join)、外链接(left/right outer join)、多表查询

    场景:在实际的项目中使用EntityFramework都会遇到使用Ef处理连接查询的问题,这里做一些小例子如何通过Linq语法处理内连接(inner join).外连接(left/right oute ...

  3. Configuration所有配置简介

    // 内存缓存的设置选项 (最大图片宽度,最大图片高度) 默认当前屏幕分辨率                // .memoryCacheExtraOptions(480, 800) // 硬盘缓存的 ...

  4. jQuery EasyUI动态添加控件或者ajax加载页面后不能自动渲染问题的解决方法

    博客分类: jquery-easyui jQueryAjax框架HTML  现象: AJAX返回的html无法做到自动渲染为EasyUI的样式.比如:class="easyui-layout ...

  5. Android Fragment动态添加 FragmentTransaction FragmentManager

    Fragment常用的三个类:android.app.Fragment 主要用于定义Fragmentandroid.app.FragmentManager 主要用于在Activity中操作Fragme ...

  6. HDU 3018 Ant Trip

    九野的博客,转载请注明出处:  http://blog.csdn.net/acmmmm/article/details/10858065 题意:n个点m条边的无向图,求用几笔可以把所有边画完(画过的边 ...

  7. (转)void指针(void *的用法)

    指针有两个属性:指向变量/对象的地址和长度 但是指针只存储地址,长度则取决于指针的类型 编译器根据指针的类型从指针指向的地址向后寻址 指针类型不同则寻址范围也不同,比如: int*从指定地址向后寻找4 ...

  8. [转] boost.circular_buffer简介

    http://www.cnblogs.com/TianFang/archive/2013/02/05/2892503.html 很多时候,我们需要在内存中记录最近一段时间的数据,如操作记录等.由于这部 ...

  9. Java基础知识强化之IO流笔记15:递归之删除带内容的目录案例

    1. 需求:递归删除带内容的目录 分析:   (1)封装目录   (2)获取该目录下的所有文件或者文件夹的File数组   (3)遍历该File数组,得到每一个File对象   (4)判断该File对 ...

  10. EasyUI-datagrid获取编辑行的数据

    可以在页面对datagrid的数据直接进行修改,然后提交到数据库,但是要求在提交前获取datagrid的所有行的数据.API提供了getData方法 最后这样写才搞定 var    arr=$(‘#d ...