Thread-Per-Message Pattern

来源:互联网 发布:淘宝子账号 编辑:程序博客网 时间:2024/05/16 05:27

什么是Thread-Per-Message Pattern?

设想一个场景,妻子在忙着淘宝,对丈夫说,“亲爱的,能不能帮我倒一下垃圾。”,然后老公去倒垃圾,老婆继续逛淘宝,这就是Thread-Per-Message Pattern,拜托别人,“这件事就交给你了”以后再回来做自己的事。

对每个命令或者请求,分配一个线程,由这个线程执行工作。


现在有这样的需求,启动三个线程,分别输出10个A,20个B,30个C,允许字母交叉,首先看代码:

首先写一个Helper类,负责对字母进行输出:

package threadPerMessage;public class Helper {    public void handle(int count, char c) {        System.out.println("        handle(" + count + ", " + c + ") BEGIN");        for (int i = 0; i < count; i++) {            slowly();            System.out.print(c);        }        System.out.println("");        System.out.println("        handle(" + count + ", " + c + ") END");    }    private void slowly() {        try {            Thread.sleep(100);        } catch (InterruptedException e) {        }    }}
然后写一个Host类,作为Helper的宿主类,对外提供helper的方法:

package threadPerMessage;public class Host {    private final Helper helper = new Helper();    public void request(final int count, final char c) {        System.out.println("    request(" + count + ", " + c + ") BEGIN");        new Thread() {            public void run() {                helper.handle(count, c);            }        }.start();        System.out.println("    request(" + count + ", " + c + ") END");    }}
最后写一个测试类Test:

package threadPerMessage;public class Test {    public static void main(String[] args) {        System.out.println("main BEGIN");        Host host = new Host();        host.request(10, 'A');        host.request(20, 'B');        host.request(30, 'C');        System.out.println("main END");    }}
回过头来看代码,在Host类中我们发现了一个匿名内部类,建立了Thread子类的实例,并且启动线程:

new Thread(){    @Override    public void run(){        helper.handle(count,c);    }}.start();
实际上是把类声明,建立实例,启动线程写在了一起。
匿名内部类:将类的声明和建立实例的操作写在一起,执行方法的时候才建立类文件。匿名类和一般类相同,都会在编译时产生出类文件。当我们在匿名内部类中用到方法的参数或者局部变量时,必须将变量声明成final,如果不是final,会发生编译错误。


Thread-Per-Message Pattern的所有参与者

1、Client(委托人)参与者

Client参与者对Host参与者发出请求,Client不知道Host如何实现这个请求,但知道他会去做。

2、Host参与者

当Host收到Client发出的请求的时候,会建立新的线程启动它,这个新的线程,会使用Helper参与者,处理这个请求。

3、Helper(帮助者)参与者

Helper参与者会对Host参与者提供处理请求的功能,Host参与者所建立的线程,会使用Helper参与者。


什么时候使用这个模式?

1、提升响应度,降低延迟时间

使用这个模式,Host对Client的响应度会提高,延迟时间会下降,尤其当处理函数很花时间或者需要等待I/O的时候,效果很明显。

2、适合在操作顺序无所谓时使用

这个模式中,handle方法执行顺序不一定时request方法调用顺序。

3、不需要返回值的时候

这个模式中,request方法不会等到handle方法执行结束,也就是说request方法拿不到handle方法的返回值。

4、应用在服务器的制作

这个模式可以在客户端送达请求,主线程接收,其他线程处理该请求的情况下使用。

5、调用方法+启动线程→传送消息

通常调用出普通方法的时候,执行完方法里的操作,控制权才会回来,然而在这个模式里request时期待才做开始的触发器,但是不会等待到执行结束(传送异步消息)。


进阶说明:进程与线程

进程与线程之间的关系,会因为平台的差异,有极大的不同,但是我们一般可以说一个进程可以建立多个线程。

线程的内存时共享的。

线程和进程最大的差异在于内存是否能共享,通常进程的内存空间是各自独立的,进程不能擅自读取、改写其他进程的内存空间。因为进程内存空间的独立性,进程无须担心被其他进程破坏。

线程则是共享内存的,所以我们进程在一条线程的内存上写入数据,而其他线程来读取。这里说的共享相同的内存,在Java中就体现为共享相同的实例。

因为线程间的内存是共享的,因此线程之间的沟通可以很自然、简单地做到。然而一位内同一个实例可以有多个线程同时访问,所以需要正确地进行互斥共享。

线程间的context-switch较容易

进程和线程的另一个差异,在于contex-switch的繁重程度。

要切换进程的时候,进程需要将当前自己的状态(context信息)存储下来,并将下一个要开始执行的进程以前所保留的context数据读回来,这个信息切换操作(context switch)所需要花费一些时间。

切换执行线程的时候,线程也需要进行context-switch操作,然而,线程所管理的context信息比进程要少,一般而言线程的context-swicth要比进程的context-switch快得多。所以需要紧密进行多项相关工作得时候,线程通常比进程来得实用。

PS、Java的内存模型中,将内存分为主存储器和工作内存两种,能够让线程共享的,只有主存储器部分。

原创粉丝点击