在生产环境中,为每一个任务分配一个线程存在一些缺陷,尤为是当须要建立大量线程时。java
也就是说,在必定范围内,增长线程能够提升系统的吞吐率,可是超过了这个范围,再建立更多的线程只会下降程序的执行速度。web
在《阿里巴巴Java手册》中提到: 编程
线程池是指管理一组同构工做线程的资源池。线程从工做队列中获取一个任务,执行任务,而后返回线程等待下一个任务。缓存
在线程池中执行任务比为每个任务分配一个线程优点更多。经过重用现有的线程而不是建立新线程,能够分摊在线程建立和销毁过程当中产生的巨大开销。当请求到达时,工做线程一般已经存在,所以不会因为等待建立线程延迟任务执行,提升了响应性。经过适当调整线程池的大小,能够建立足够多的线程使得处理器保持忙绿,同时还能够防止过多线程互相竞争使得内存耗尽。服务器
所以应该使用线程池。其目的包括:多线程
Executor框架是在java.util.concurrent包下,在Java 5中引入的。经过Executor来启动线程比使用Thread.start()方法更好,更易于管理且效率更高(用线程池实现节约了开销),而且还有助于避免this逸出。并发
Executor两级调度模型以下。用户将多个任务提交给Executor框架,框架在线程池中分配线程执行它们。而后操做系统再将这些线程分配给处理器执行。 框架
Executor是个简单的接口,它支持多种不一样类型的任务执行策略,提供了一种标准的方法将任务的提交过程与执行过程解耦,并用Runnable表示任务。Executor的实现还提供了对生命周期的支持。异步
Executor接口中之定义了一个方法execute(Runnable command),该方法接收一个Runable实例,它用来执行一个任务,任务即一个实现了Runnable接口的类。socket
Executor基于生产者-消费者模式,提交任务的操做至关于生产者,执行任务的线程至关于消费者。
class TaskExecWebServer{
private static final int NTHREADS=100;
private static final Executor executor=Executors.newFixedThreadPool(NTHREADS);
public static void main(String[] args) throws IOException {
ServerSocket serverSocket=new ServerSocket(8080);
while(true){
final Socket socket=serverSocket.accept();
Runnable task=new Runnable() {
@Override
public void run() {
System.out.println(socket);
}
};
executor.execute(task);
}
}
}
class ThreadPerTaskExecutor implements Executor{
@Override
public void execute(Runnable command) {
new Thread(command).start();
}
}
class WithinThreadExecutor implements Executor{
@Override
public void execute(Runnable command) {
command.run();
}
}
复制代码
在上述代码中,咱们首先在webServer中使用了一个固定大小为100的线程池。而后使用executor.execute(task)来提交线程。在执行的过程当中,能够采用不一样的执行方法。能够为每一个请求启动一个新的线程,如ThreadPerTaskExecutor,还能够以同步方式执行全部任务,如WithinThreadExecutor。这样就实现了把任务的提交和执行解耦。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
复制代码
corePoolSize
是线程池的基本大小。maximumPoolSize
是线程池的最大线程大小。keepAliveTime
和unit
是线程空闲后的存活时间workQueue
是用于存听任务的阻塞队列handler
是当队列和最大线程池都满了以后的饱和策略为了解决执行服务的生命周期问题,Executor拓展了ExecutorService接口,添加了一些用于生命周期管理的方法。ExecutorService的生命周期有三种状态:运行、关闭和已终止。
在初始建立时,ExecutorService处于运行状态。使用shutdown()方法将再也不接受新的任务,同时等待提交的任务都执行完成以后再关闭。调用shutdownNow()方法,至关于调用了每一个线程的interrupt()方法,将尝试取消全部运行中的任务,而且再也不启动队列中还没有开始执行的任务。
在ThreadPoolExecutor中,定义了以下状态:
Executors提供了一系列工厂方法用于建立线程池,返回的线程池都实现了ExecutorService接口。
建立了固定线程数目的线程池,每当提交一个任务时就建立一个县城,直到达到线程池的最大数量。当线程池满时,有新的线程要创建,只能放在另外的队列中等待,直到当前的线程中某个线程被移出。
针对一些稳定的并发线程,多用于服务器。
建立了一个能够缓存的线程池,没有规模限制。调用execute将重用之前构造的线程(若是线程可用)。若是当前没有可用线程,则建立一个新线程;若是当前规模超过了处理需求,将从缓存中终止并移除已经有60秒未使用的线程。
放入该线程池的线程超过timeout不活动,会被自动终止,缺省设置为60秒。
一般用于执行一些生存期很短的异步型任务。
建立了一个单线程的Executor,建立了单个线程来执行任务,若是线程异常结束,会建立另外一个线程来替代。
确保依照任务在队列中的顺序来串行执行(FIFO,LIFO,优先级)。
注意
与前几种不一样的是,这个方法返回的是ScheduledExecutorService,前边三种返回的是ExecutorService。建立了一个固定长度的线程池,而且是支持定时以及周期性的任务执行的线程池。多数状况下用来替代Timer类。
Timer用来负责管理延迟任务以及周期任务,可是它存在一些缺陷,应该使用与ScheduledThreadPoolExecutor来替代它。
Timer类在执行全部定时任务时只会建立一个线程,当有多个定时任务时,就会产生延迟。
当多个定时任务中有一个任务抛出异常,全部的任务都没法执行。
Timer执行周期任务时依赖系统时间。后者不会因为系统时间的改变而发生执行的变化。
任务分为两类,一类是实现了Runnable接口的类,一类是实现了Callable接口的类,都能被Executor执行。
Runnable是一种有局限的抽象,由于它不能返回值或者抛出一个受检查的异常。
Callable的call()方法相似于Runnable的run()方法,可是call()方法有返回值,而且可能抛出一个异常。
任务的生命周期包括:建立,提交,开始和完成。在Executor框架中,已经提交但还没有开始的任务能够取消,对于已经开始执行的任务,只有当它们响应中断时,才能取消。
向线程池中提交线程的时候有两种方法:execute()和submit()。
execute()提交只能提交一个Runnable对象,且返回值是void。也就是说提交后若是线程运行,和主线程就脱离了关系。
submit()提交:
submit()方法能够提交一个Callable接口的对象,也能提交一个Ruuable的对象。使用这种方式提交会返回一个Future对象,表明了该线程的执行结果,主线程经过get方法获取到从线程中返回的结果数据。使用Future能够表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消;还能获取任务的结果;并能取消任务。
Future的get()方法:
将executor与blockingqueue融合在一块儿,将Callable任务交由它执行,使用相似队列操做的take和poll等方法得到future。也就是在构造函数中建立了阻塞队列来保存执行完成的结果。
使用Future.get(long,timeType)为任务设计时限,时限内有结果则返回,超时则抛出TimeOutException
支持时限的还有invokeAll()方法,它将多个任务提交给ExecutorService并获取结果。其参数为一组任务,返回一组Future,按照任务集合中的顺序将Future添加到返回的集合,实现了两者的关联。当全部任务执行完毕,或超市,或中断,该方法将返回。经过get或isCancelled来判断每一个任务的执行状况。