Thread-Per-Message Pattern

什么是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的内存模型中,将内存分为主存储器和工作内存两种,能够让线程共享的,只有主存储器部分。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值