Executor 框架及线程池的使用

2016-08-13 19:10:32   最后更新: 2016-08-17 19:15:24   访问数量:426




前面的日志中,我们主要介绍了 java 并发编程的基本内容和一些基本工具类

我们已经知道,很多情况下,单线程线性执行的效率是不可以接受的,而为每个任务都创建线程,可能会无限制的创建下去,资源消耗会异常显著,同时,线程生命周期的开销非常高,不断创建与销毁造成了不必要的代价,而 JVM 还有这 thread 数、栈大小的限制,无线分配线程下去会很容易抛出 OutOfMemoryError,而这样的错误是非常危险的

因此,在一定范围内,增加线程可以提高系统吞吐量,但如果超出了这个范围,再创建更多线程只会降低程序的执行速度,并且可能造成整个应用程序的崩溃

很多时候,在原型设计和开发阶段,无线创建线程的模型可以很完美的运行,但是在高负载下运行或有人攻击你的接口的时候,这个隐患才会爆发出来

 

此前,我们介绍过如何通过有界队列来防止高负荷的应用程序耗尽内存:

java 中线程安全的容器类

线程池则是另一种十分简化的线程管理工具

 

与上述模式相比,线程池具有显而易见的优势,顾名思义,线程池是一个缓存工作线程的资源池,当请求到来,通常无需重新创建线程,而是直接使用池中的线程,这样就减少了创建和销毁线程的巨大开销,同时也提高了响应速度

通过调整线程池中线程数的多少,可以有效地保持处理器的忙碌状态,充分利用资源,也可以防止果多线程相互竞争资源而造成的资源耗尽

 

java.util.concurrent 提供了一种灵活的线程池实现作为 Executor 框架的一部分,任务执行的主要抽象不再是 Thread 而是 Executor

任务执行框架让各种调优、管理、监控、错误报告、日志记录等功能变得容易起来

 

Excecutor 是一个简单的接口:

public interface Executor { void execute(Runnable command); }

 

Executor 也是基于生产者-消费者模式的,提交任务的操作相当于生产者生产消息,执行任务相当于消费者消费消息,因此,在项目中,如果需要实现一个生产者-消费者模型,通常最简单的方式就是使用 Executor

下面是一个使用 Executor 框架实现的简单的 web 服务器:

package com.techlog.test.service; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.Executor; import java.util.concurrent.Executors; public class ConcurrentCall { private static final int NTHREADS = 100; private static final Executor exec = Executors.newFixedThreadPool(NTHREADS); public static void main(String[] args) throws IOException { ServerSocket socket = new ServerSocket(80); while (true) { final Socket connection = socket.accept(); exec.execute(new Runnable() { @Override public void run() { handleRequest(connection); } }); } } public static void handleRequest(Socket connection) { // DO SOMETHING } }

 

 

jvm 只有在所有非守护线程全部终止后才会退出,因此,如果无法正确关闭 Executor,那么 JVM 将无法结束

Executor 的生命周期有三种状态:运行、关闭、终止

Executor 接口继承了 Executor,为他添加了一些生命周期管理方法,Executor 创建时处于运行状态,当调用 ExecutorService.shutdown 方法后,Executor 开始平缓关闭,不再接受任何新的任务,此时,isShutdown() 方法将返回 true,同时,这时的 Executor 不能添加任何任务,ExecutorService 的 shutdownNow 方法则会强制关闭,他会取消所有运行中的任务,并且所有队列中的任务都将不会被执行

当 Executor  中的任务执行完毕后,他就处于终止状态,isTerminated 方法将返回 true

 

Executors 类提供了一系列工厂方法用于创建线程池,他的一系列工厂方法实现了不同的 ExecutorService,而 ExecutorService 则继承了 Executor

  • public static ExecutorService newFixedThreadPool(int nThreads) -- 创建指定数目线程的线程池
  • public static ExecutorService newCachedThreadPool() -- 创建一个可缓存的线程池,调用 execute 将重用已有的线程,如果没有线程可用,则创建新的线程加入到线程池中,持续 60 秒未被使用的线程则会从线程池中被移除
  • public static ExecutorService newSingleThreadExecutor() -- 创建单线程化的 Executor
  • public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) -- 创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类

 

newFixedThreadPool 和 newCachedThreadPool 是最常用的线程池工厂方法

 






技术帖      龙潭书斋      线程      thread      线程池      execute      threadpool      executor     


京ICP备15018585号