早期的应用程序大可能是单线程串行执行的,虽然程序的任务边界清晰有序,可是执行的效率却很低,尤为是执行花费时间较长的操做,会致使大量的等待和堆积。为了提升程序的执行效率和吞吐量,咱们很天然的会想到多线程,即为每一个任务都新建一个独立的线程,这样就极大地提升了程序的执行效率。但事实上多线程也会带来不少问题。好比大量的建立线程,这自己就会消耗不少的资源,尤为是内存,当建立的线程数量超过服务器可以承受的极限时,内存溢出是在所不免的;好比还有其余稳定性问题,以及多线程形成的程序调用和管理的混乱。因此,综上所述,咱们须要一个介于二者之间的工具,既能够建立大量的线程来提升程序的并发性和吞吐量,同时又能够有序的管理这些线程,可以可控。而Executor接口和线程池技术就是在这种背景下应运而生的。下面分别将这两种经常使用的并发技术作以介绍和总结。java
java.util.coucurrent包下面为咱们提供了丰富的并发工具,Executor和ExecutorService接口就是其中比较重要的两个。缓存
public interface Executor{ void execute(Runnable command); }
以上是Executor接口的代码,能够看出这个借口很是简单,就只有一个execute()方法。但他却为强大的异步任务执行提供了基础,它支持不一样类型的任务执行策略。Executor框架基于生产者消费者模型,它提供了一个标准的方法将任务的提交和任务的执行过程解耦,并用Runnable来表示任务。下面来看一个基于Executor的简单服务器实现:服务器
public class ExecutorWebServer { private static final int LIMIT = 50; private static final Executor exe = Executors.newFixedThreadPool(LIMIT); public static void main(String[] args) throws IOException { // TODO Auto-generated method stub ServerSocket server = new ServerSocket(80); while(true){ Socket socket = server.accept(); Runnable task = new Runnable(){ public void run(){ doSomeThing(socket); } }; exe.execute(task); } } }
经过上面列子咱们就很好的把任务的提交和任务的执行分开来,这就是Executor框架最大的优点。多线程
上面咱们讲了如何建立一个Executor,可是并无讲如何关闭它。既然Executor是为应用程序服务的,于是他们应该是可关闭的,并将关闭操做中受影响的任务状态反馈给应用程序。为了解决执行服务的生命周期问题,ExecutorService扩展了Executor接口,增长了管理生命周期的方法:并发
public interface ExecutorService Extends Executor{ void shutdown(); boolean isShutDown(); boolean isTerminated(); …… }
ExecutorService的生命周期分为三种,运行,关闭和已终止。下面看这个简单的实例:框架
class NetworkService implements Runnable { private final ServerSocket serverSocket; private final ExecutorService pool; public NetworkService(int port, int poolSize) throws IOException { serverSocket = new ServerSocket(port); pool = Executors.newFixedThreadPool(poolSize); } public void run() { // run the service try { for (;;) { pool.execute(new Handler(serverSocket.accept())); } } catch (IOException ex) { pool.shutdown(); } } } class Handler implements Runnable { private final Socket socket; Handler(Socket socket) { this.socket = socket; } public void run() { // read and service request on socket } }
能够经过Executors类的几个静态方法来建立线程池。包含如下几类线程池:
newFixedThreadPool建立固定长度的线程池。每提交一个任务时就新建一个线程,直到达到线程池的最大数量。异步
newCachedThreadPool建立一个可缓存的线程池,线程的数量不受限制,可是在之前线程可用时将重用他们。socket
newSingleThreadExecutor建立一个使用单个 worker 线程的 ,以无界队列方式来运行该线程。函数
newScheduleThreadPool建立一个线程池,它可安排在给定延迟后运行命令或者按期地执行。相似于一个定时器。工具
线程池的理想大小取决于被提交任务的类型以及所部署系统的特性。在代码中最好不要固定线程池的大小,而要经过某种灵活机制来配置。线程池大小的配置只要避免“过大”和”太小“两种极端状况便可。线程池过大会致使大量的线程竞争CPU和内存,最终致使资源耗尽;线程池太小时又会致使资源浪费,因此设置一个合适的线程池大小很是重要。一般有一个简单的设置线程池大小的公式供咱们参考使用:
N(Threads) = N(cpu) * U(cpu) * (1 + w/c)
N(cpu)表明cpu的个数,U(cpu)表明cpu利用率, w/c表示等待时间与计算时间的比值
ThreadPoolExecutor为Executor和ExecutorService接口提供了基本实现。下面咱们来看一下ThreadPoolExecutor类的构造函数:该类一共有四个构造函数,其中最基础的一个构造函数以下:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
corePoolSize表示的是核心池的大小,第二个参数表示线程池最大的线程数,第三个参数表示存活时间,第四个参数表示给定单元粒度的时间段,第五个参数表示的是工做队列。
ThreadPoolExecutor是能够扩展的,它提供了几个能够在子类中改写的方法:beforeExecutor,afterExecutor,terminated,这些方法可用于扩展ThreadPoolExecutor的行为。