在直接使用创建线程Thread-Per-Message模式中,每次都会创建新线程导致占用了大量的系统资源,在占用过多的资源后可能导致OOM,于是就需要一种Work Thread模式的出现,导致出现了线程池的思想
对应到现实生活中,就可以类比为一个工厂中,多个工人去干活,有活一起干,没活聊聊天,在编程领域和现实领域中,工人的数量都是一定的
那么线程池就是基于这种思想进行的创建,为了避免线程的创建和销毁导致的占用性能过高
将线程放在一个对象池中进行了保护起来,接下来就是利用这个线程池去解决之前的echo问题
仅仅是将线程改为从线程池之中获取,然后进行任务的提交处理
ExecutorService es = Executors.newFixedThreadPool(500);
final ServerSocketChannel ssc = ServerSocketChannel.open().bind(new InetSocketAddress(8080));
//处理请求
try {
while (true) {
// 接收请求
SocketChannel sc = ssc.accept();
// 将请求处理任务提交给线程池
es.execute(()->{
try {
// 读Socket
ByteBuffer rb = ByteBuffer.allocateDirect(1024);
sc.read(rb);
//模拟处理请求
Thread.sleep(2000);
// 写Socket
ByteBuffer wb =
(ByteBuffer)rb.flip();
sc.write(wb);
// 关闭Socket
sc.close();
}catch(Exception e){
throw new UncheckedIOException(e);
}
});
}
} finally {
ssc.close();
es.shutdown();
}
同时,在使用线程池的时候,为了避免无限制的创建线程导致OOM,避免无限制的接受任务导致OOM,一定要创建有界的队列来接受任务,避免OOM问题的出现,同时,在使用了有界的队列之后,需要制定明确的拒绝策略
最好在线程的工厂中制定相对应的线程名字,方便去查询业务相关的信息
其次,是为了避免线程死锁,一定要一个线程池对应一个任务,如果多个任务之间具有等待关系,一定要用多个线程池,不然,可能导致所有的线程都被用来等待了,没有真正去运行任务
这个模式和Thread-Per-Message模式之间最大的区别在于,直接使用一个线程去异步执行请求,我们是知道是谁去做的,但是使用线程池去执行一个请求,那么就无法知道具体是谁去做的