1、为什么要用线程池?
服务器经常出现处理大量单个任务处理的时间很短而请求的数目却是巨大的请求。
构建服务器应用程序的一个过于简单的模型应该是:每当一个请求到达就创建一个新线程,然后在新线程中为请求服务。实际上,对于原型开发这种方法工作得很好,但如果试图部署以这种方式运行的服务器应用程序,那么这种方法的严重不足就很明显。每个请求对应一个线程(thread-per-request)方法的不足之一是:为每个请求创建一个新线程的开销很大;为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。
除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目。
线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。
2、四种线程池
1、newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
public class FixedThreadPoolTest {
//创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
private static void testFixedThreadPool() throws InterruptedException, ExecutionException {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
System.out.println("当前线程数:" + Runtime.getRuntime().availableProcessors());
fixedThreadPool.shutdown();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
testFixedThreadPool();
}
}
可以将上面程序复制下来,运行,看运行结果就可以发现,是三个线程在运行,然后再接下来三个,最后一句打印,当前线程数为4,因为还有主线程。
2、newCachedThreadPool创建一个可缓存的线程池。这种类型的线程池特点是:
1).工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
2).如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。
public class CachedThreadPoolTest {
//创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
private static void testCachedThreadPool() throws InterruptedException, ExecutionException {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
Thread.sleep(index * 1000);
cachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(index);
}
});
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
testCachedThreadPool();
}
}
3、newSingleThreadExecutor创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行(特色)。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的 。
public class SingleThreadExecutorTest {
//创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
private static void testSingleThreadExecutor() throws InterruptedException, ExecutionException {
ExecutorService singleThreadExecutor = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
public void run() {
while (true) {
System.out.println(index);
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread.sleep(500);
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
testSingleThreadExecutor();
}
}
4、newScheduleThreadPool创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。
public class ScheduledThreadPoolTest {
//创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
private static void testScheduledThreadPool() throws InterruptedException, ExecutionException {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
testScheduledThreadPool();
}
} 总结: FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源,如果需要关闭,需要手动关闭。