一. Thread-Per-Message介绍
所谓Per, 就是 "每~" 的意思, 因此, Thread-Per-Message 模式直译过来就是 "每一个消息一个线程" 的意思, Message 在这里可以理解为 "请求" 或 "命令", 为每个命令或请求分配一个新的线程, 由这个新线程来执行处理----这就是 Thread-Per-Message 模式, 在该模式中, 消息的 "委托端" 和 "执行端" 是不同的线程, 消息的委托端线程会告诉执行端的线程 "这项任务交给你执行了"
二. 示例程序
类名 | 说明 |
Main | 向Host发送字符的请求类 |
Host | 针对请求创建线程的类 |
Helper | 提供字符显示功能的被动类 |
2.1 Main类
Main 类会创建一个 Host 类的实例, 然后调用 Host 的 request() 方法, 为了了解线程的运行状况, 我们在 Main 类的开头和结尾加上了调试输出
public class Main {
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");
}
}
2.2 Host类
Host 类的 request() 方法新启动了一个线程, 实例操作将有该线程来执行
声明 Thread 类, 并重写 run() 方法, 创建该类的实例, 调用 start() 方法启动线程
匿名内部类的 run() 方法使用了 request() 方法的参数 count 和 c, 这样, 当匿名内部类用到方法的参数或局部变量时, 这些变量必须声明为 final, 否则会出现编译错误
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() {
@Override
public void run() {
helper.handle(count, c);
}
}.start();
System.out.println(" request (" + count + ", " + c + ") end");
}
}
2.3 Helper类
Helper 类提供了一个用于按指定次数显示字符的 handler() 方法, 另外, 为了延缓显示速度(也就是模拟 handler()方法操作很费时, 使用了 Thread.sleep() 方法
public class Helper {
public void handle(int count, char c) {
System.out.println(" handler (" + count + ", " + c + ") begin");
for (int i = 0; i < count; i++) {
slowly();
System.out.println(c);
}
System.out.println("");
System.out.println(" handler (" + count + ", " + c + ") end");
}
private void slowly() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
//
}
}
}
2.4 运行示例
运行结果示例如图, 我们仔细查看调试信息信息会发现, 在 handler() 方法终止之前, request() 方法已经运行完毕终止了, 执行 main() 方法的主线程在连续调用 host 的 request() 方法之后, 就会立即终止, 不管 handler() 方法花费多长时间处理, 都不会影响 request() 方法的响应性, 这是因为, request() 方法并不会等待 handler() 方法执行结束, 而是立即返回
main begin
request (10, a) begin
request (10, a) end
request (20, b) begin
request (20, b) end
request (30, c) begin
handler (10, a) begin
handler (20, b) begin
handler (30, c) begin
request (30, c) end
main end
bcabcacbacbacbacbacbacbacbacba
handler (10, a) end
bcbcbcbcbcbcbcbcbcb
handler (20, b) end
ccccccccccc
handler (30, c) end
三. Thread-Per-Message模式中的登场的角色
3.1 Client(委托人)
Client 会向 host 发送请求, 但并不知道 Host 是如何实现该请求的, 在示例程序中, 有 Main 类扮演此角色
3.2 Host
Host 收到 Client 角色的请求, 会创建并启动一个新线程, 新创建的线程将使用 Helper 来处理请求, 在示例程序中, 有 Host 类扮演此角色
3.3 Helper
Helper 角色提供请求处理的功能, Host 创建的新线程会利用 Helper
3.5 模式类图
四. java.util.concurrent包和Thread-Pre-Message模式
实际开发中, 是不能使用 new Thread() 这种方式显式创建线程的,所以本节将介绍 "启动线程" 的处理的各种实现形式, 来看一下抽象程序变化的 java.util.concurrent 包
4.1 java.util.concurrent.ThreadFactory接口
该接口声明了一个 new Thread() 方法, 将线程的创建抽象成了接口, 参数中的 Runnable 对象表示线程执行的具体操作, 使用 new 创建线程时, 代码将依赖与 Thread 类, 我们无法控制创建线程的部分, 可复用性低, 假如我们用字段 threadFactory 来保存 ThreadFactory 对象, 用 threadFactory.newThread() 来替代 new Thread(), 这样一来, 只要替换赋值给 threadFactory 的 ThraedFactory 对象, 我们便可以控制线程的创建了, 这就是工厂方法模式, 修改后的示例程序如下
public class Main {
public static void main(String[] args) {
System.out.println("main begin");
Host host = new Host(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
});
host.request(10, 'a');
host.request(20, 'b');
host.request(30, 'c');
System.out.println("main end");
}
}
public class Host {
private final Helper helper = new Helper();
private final ThreadFactory threadFactory;
public Host(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
}
public void request(final int count, final char c) {
System.out.println(" request (" + count + ", " + c + ") begin");
threadFactory.newThread(new Runnable() {
@Override
public void run() {
helper.handle(count, c);
}
});
System.out.println(" request (" + count + ", " + c + ") end");
}
}
4.2 java.util.concurrent.Executors类获取的ThreadFactory
java.util.concurrent.Executors 类提供了很多使用的静态方法, 如 Executors.defaultThreadFactory() 方法可以获取当前默认设置的 ThreadFactory 对象, 上面 Host 类通过构造函数接收 ThreadFactory 对象, 如果使用下面的 Main 类, 那么线程的创建方式则变为默认设置, 前面的代码完全不需要修改, 这也就表明代码的复用性很高
public class Main {
public static void main(String[] args) {
System.out.println("main begin");
Host host = new Host(Executors.defaultThreadFactory());
host.request(10, 'a');
host.request(20, 'b');
host.request(30, 'c');
System.out.println("main end");
}
}
4.3 java.util.concurrent.Executor接口
该接口声明了一个 execute() 方法, Executor接口将某些 "处理的执行" 抽象化了, 参数传入的 Runnable 对象表示 "执行的处理" 的内容, 前面介绍 ThreadFactory 接口隐藏了线程创建的细节, 但并没有隐藏线程创建的方法, 如果使用 Executor 接口, 创建线程的操作也可以隐藏起来, 使用 Executor 接口的 Host 类, 既没有使用 Thread, 也没有使用 ThreadFactory, Host 类如何执行 Runnable 对象取决于构造函数中传入的 Executor 对象, 也就是说, 利用 Host 类可以控制处理的执行, 比如说我这里传入了线程池对象, 那么 Host 类就使用线程池对象执行 Runnable 对象, 修改的代码如下
public class Main {
public static void main(String[] args) {
System.out.println("main begin");
Host host = new Host(new ThreadPoolExecutor(1, 1, 1,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()
, Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy()));
host.request(10, 'a');
host.request(20, 'b');
host.request(30, 'c');
System.out.println("main end");
}
}
public class Host {
private final Helper helper = new Helper();
private final Executor executor;
public Host(Executor executor) {
this.executor = executor;
}
public void request(final int count, final char c) {
System.out.println(" request (" + count + ", " + c + ") begin");
executor.execute(new Runnable() {
@Override
public void run() {
helper.handle(count, c);
}
});
System.out.println(" request (" + count + ", " + c + ") end");
}
}
4.4 java.util.concurrent.ExecutorService接口
再前面的程序中, 虽然使用了 Executor 进行了抽象化, 但最终还是需要自己手动执行 new Thread(), 不过并以一定都必须创建线程, 只要遵循 Executor 接口, 我们也可以使用某些 "会复用那些处理执行结束后空闲下来的线程" 的类, 这就是 java.util.concurrent.ExecutorService 接口, 该接口对反复 execute 的服务进行了抽象化, 线程会一直在后台运行着, 每当调用 execute 方法时, 线程就会执行 Runnable 对象, 通常情况下, 需要使用 shutdown() 方法来结束服务, 由 ExecutorService接口是 Executor 的子接口, 所以接受 Executor 对象的 Host 类也可以接收 ExecutorService 对象, 修改后的代码如下
public class Main {
public static void main(String[] args) {
System.out.println("main begin");
// return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
// 60L, TimeUnit.SECONDS,
// new SynchronousQueue<Runnable>());
ExecutorService executor = Executors.newCachedThreadPool();
Host host = new Host(executor);
try {
host.request(10, 'a');
host.request(20, 'b');
host.request(30, 'c');
System.out.println("main end");
} finally {
executor.shutdown();
}
}
}
4.5 java.util.concurrent.ScheduledExecutorService接口
该接口是 ExecutorService 的子接口, 用于推迟操作的执行, 代码中的 Host 类的构造函数会接受 ScheduledExecutorService 对象, 然后 request() 方法会将 ScheduledExecutorService 对象调度为 "请求到达3秒之后再执行", 这里调用 schedule 方法位于 ScheduledExecutorService 接口中, 可设置 Runnable 对象和 延迟时间(delay), 时间单位(unit), 修改后的代码如下
public class Main {
public static void main(String[] args) {
System.out.println("main begin");
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
Host host = new Host(scheduledExecutorService);
try {
host.request(10, 'a');
host.request(20, 'b');
host.request(30, 'c');
System.out.println("main end");
} finally {
scheduledExecutorService.shutdown();
}
}
}
public class Host {
private final Helper helper = new Helper();
private final ScheduledExecutorService scheduledExecutorService;
public Host(ScheduledExecutorService scheduledExecutorService) {
this.scheduledExecutorService = scheduledExecutorService;
}
public void request(final int count, final char c) {
System.out.println(" request (" + count + ", " + c + ") begin");
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
helper.handle(count, c);
}
}, 3, TimeUnit.SECONDS);
System.out.println(" request (" + count + ", " + c + ") end");
}
}