在Java中,若是每当一个请求到达就建立一个新线程,开销是至关大的。在实际使用中,每一个请求建立新线程的服务器在建立和销毁线程上花费的时间和消耗的系统资源,甚至可能要比花在实际处理实际的用户请求的时间和资源要多的多。除了建立和销毁线程的开销以外,活动的线程也须要消耗系统资源。若是在一个JVM中建立太多的线程,可能会致使系统因为过分消耗内存或者“切换过分”而致使系统资源不足。为了防止资源不足,服务器应用程序须要一些办法来限制任何给定时刻处理的请求数目,尽量减小建立和销毁线程的次数,特别是一些资源耗费比较大的线程的建立和销毁,尽可能利用已有对象来进行服务,这就是“池化资源”技术产生的缘由。
线程池主要用来解决线程生命周期开销问题和资源不足问题,经过对多个任务重用线程,线程建立的开销被分摊到多个任务上了,并且因为在请求到达时线程已经存在,因此消除了建立所带来的延迟。这样,就能够当即请求服务,使应用程序响应更快。另外,经过适当的调整线程池中的线程数据能够防止出现资源不足的状况。java
JDK 1.5之后,Java提供一个线程池ThreadPoolExecutor类。下面从构造函数来分析一下这个线程池的使用方法。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
参数名、说明:
* corePoolSize 线程池维护线程的最少数量
* maximumPoolSize 线程池维护线程的最大数量
* keepAliveTime 线程池维护线程所容许的空闲时间
* workQueue 任务队列,用来存放咱们所定义的任务处理线程
* threadFactory 线程建立工厂
* handler 线程池对拒绝任务的处理策略
ThreadPoolExecutor将根据corePoolSize和maximumPoolSize设置的边界自动调整池大小。当新任务在方法execute(Runnable) 中提交时, 若是运行的线程少于corePoolSize,则建立新线程来处理请求。
若是正在运行的线程等于corePoolSize时,ThreadPoolExecutor优先往队列中添加任务,直到队列满了,而且没有空闲线程时才建立新的线程。若是设置的corePoolSize 和 maximumPoolSize 相同,则建立了固定大小的线程池。
keepAliveTime:当线程数达到maximumPoolSize时,通过某段时间,发现多出的线程出于空闲状态,就进行线程的回收。keepAliveTime就是线程池内最大的空闲时间。
workQueue:当核心线程不能都在处理任务时,新进任务被放在Queue里。web
ThreadPoolExecutor是Executors类的实现,Executors类里面提供了一些静态工厂,生成一些经常使用的线程池,主要有如下几个:
1. newSingleThreadExecutor:建立一个单线程的线程池。这个线程池只有一个线程在工做,也就是至关于单线程串行执行全部任务。若是这个惟一的线程由于异常结束,那么会有一个新的线程来替代它。此线程池保证全部任务的执行顺序按照任务的提交顺序执行。
2. newFixedThreadPool:建立固定大小的线程池。每次提交一个任务就建立一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,若是某个线程由于执行异常而结束,那么线程池会补充一个新线程。(我用的就是这个,同上所述,至关于建立了相同corePoolSize、maximumPoolSize的线程池)
3. newCachedThreadPool:建立一个可缓存的线程池。若是线程池的大小超过了处理任务所须要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增长时,此线程池又能够智能的添加新线程来处理任务。此线程池不会对线程池大小作限制,线程池大小彻底依赖于操做系统(或者说JVM)可以建立的最大线程大小。缓存
测试类:ThreadPool,建立了一个 newFixedThreadPool,最大线程数为3的固定大小线程池。而后模拟10个任务丢进去。主线程结束后会打印一句:主线程结束。服务器
public class ThreadPool {
public static void main(String[] args) {
//建立固定大小线程池
ExecutorService executor = newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
SendNoticeTask task = new SendNoticeTask();
task.setCount(i);
executor.execute(task);
}
System.out.println("主线程结束");
}
}
//测试类:SendNoticeTask,执行任务类,就是打印一句当前线程名+第几个任务。为了方便观察,每一个线程执行完之后睡10s。
public class SendNoticeTask implements Runnable {
private int count;
public void setCount(int count) {
this.count = count;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " start to" + " send " + count + " ...");
try {
Thread.currentThread().sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("finish " + Thread.currentThread().getName());
}
}
执行结果:多线程
主线程结束
pool-1-thread-1 start to send 0 ...
pool-1-thread-2 start to send 1 ...
pool-1-thread-3 start to send 2 ...
finish pool-1-thread-1
pool-1-thread-1 start to send 3 ...
finish pool-1-thread-2
finish pool-1-thread-3
pool-1-thread-2 start to send 4 ...
pool-1-thread-3 start to send 5 ...
finish pool-1-thread-1
pool-1-thread-1 start to send 6 ...
finish pool-1-thread-2
finish pool-1-thread-3
pool-1-thread-2 start to send 7 ...
pool-1-thread-3 start to send 8 ...
finish pool-1-thread-1
pool-1-thread-1 start to send 9 ...
finish pool-1-thread-2
finish pool-1-thread-3
finish pool-1-thread-1
由上可见执行顺序是这样的:线程池建立了三个线程,分别执行任务0、一、2,因为线程建立须要必定时间,所以前三个线程的执行顺序具备必定随机性。此时主线程接着往线程池中塞任务,线程池已达到最大线程数(3),因而开始往队列里放。当某个线程执行完任务后,直接从队列里拉出新的任务执行,队列具备先进先出的特性,所以后面的任务执行是有序的。
这个看一下 Executors 类的源码就更明白了。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());}
其实是建立了一个具备固定线程数、无界队列的 ThreadPoolExecutor。队列无界,不会拒绝任务提交,所以使用此方法时,须要注意资源被耗尽的状况。
测试类:TestThreadPool,采用有界队列(队列大小2),和默认拒绝策略的ThreadPoolExecutor。ide
public class TestThreadPool {
private static final int corePoolSize = 2;
private static final int maximumPoolSize = 4;
private static final int keepAliveTime = 1000;
private static BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(2);
public static void main(String[] args) {//建立线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, workQueue);
for (int i = 0; i < 10; i++) {
SendNoticeTask task = new SendNoticeTask();
task.setCount(i);
executor.execute(task);
}
System.out.println("主线程结束:" + Thread.currentThread().getName());
}
}
执行结果:svg
Exception in thread "main" pool-1-thread-2 start to send 1 ...
pool-1-thread-3 start to send 4 ...
pool-1-thread-4 start to send 5 ...
pool-1-thread-1 start to send 0 ...
java.util.concurrent.RejectedExecutionException: Task demo.SendNoticeTask@32d16dc8 rejected from java.util.concurrent.ThreadPoolExecutor@15e6e48b[Running, pool size = 4, active threads = 4, queued tasks = 2, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2048)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:821)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1372)
at demo.TestThreadPool.main(TestThreadPool.java:24)
finish pool-1-thread-2
pool-1-thread-2 start to send 2 ...
finish pool-1-thread-3
pool-1-thread-3 start to send 3 ...
finish pool-1-thread-4
finish pool-1-thread-1
finish pool-1-thread-2
finish pool-1-thread-3
线程池建立了2个线程,分别执行任务0、1,线程池达到corePoolSize,新进任务二、3被放入队列中等待处理,此时队列满,而线程池中线程没有执行完任务0、1,线程池建立新的线程,执行新进任务四、5,达到maximumPoolSize。此时全部任务都没有执行结束,主线程又继续提交任务,线程池进入默认异常策略(AbortPolicy)拒绝服务。函数