线程池的作用:线程池是为突发大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需要的时间,从而提高效率。
如果一个线程的时间非常长,就没必须使用线程池了(不是不能作长时间操作,而是不适合)。
ThreadPool中的线程不用手动开始,也不能手动取消,要做的只是把工作函数排入线程池,剩下的工作将由系统自动完成,也就是说我们不能控制线程池中的线程。如果想对线程进行更多的控制,那么就不适合使用线程池。在以下情况中不宜使用ThreadPool类而应该使用单独的Thread类:
1、线程执行需要很长的时间;
2、需要为线程指定详细的优先级;
3、在执行过程中需要对线程进行操作,比如睡眠、挂起等。
所以ThreadPool适合于并发运行若干个运行时间不长且互不干扰的函数。
与每次需要时都创建线程相比,线程池可以降低创建线程的开销,这是因为线程池在线程执行结束后进行的是回收操作,而不是真正的销毁线程。使用线程池的方式是复用线程的,而不使用线程池的方式是每次都要创建线程。
程序代码的运行时间的比较:
public classThreadPoolTest {
privatestatic int count = 200000;
privatestatic ThreadPoolExecutor tp = new ThreadPoolExecutor(1, 1, 60,TimeUnit.SECONDS,
newLinkedBlockingDeque<Runnable>(count));
privatefinal List<Integer> l = new LinkedList<Integer>();
public void ThreadPool(){
doublestartTime = System.currentTimeMillis();
finalRandom random = new Random();
for(inti = 0; i < count; i ++){
tp.execute(newRunnable() {
@Override
publicvoid run() {
//TODO Auto-generated method stub
l.add(random.nextInt());
}
});
}
tp.shutdown();
try{
tp.awaitTermination(1,TimeUnit.DAYS);
}catch(InterruptedExceptione){
e.printStackTrace();
}
doubleendTime = System.currentTimeMillis();
System.out.println(endTime- startTime);
System.out.println(l.size());
}
public void ThreadNotPool(){
doublestartTime = System.currentTimeMillis();
finalRandom random = new Random();
for(inti = 0; i < count; i ++){
Threadthread = new Thread(){
@Override
publicvoid run(){
l.add(random.nextInt());
}
};
thread.start();
try{
thread.join();
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
doubleendTime = System.currentTimeMillis();
System.out.println(endTime- startTime);
System.out.println(l.size());
}
}
运行时间的比较:
ThreadPool: 250ms
ThreadNotPool: 19514ms
从结果中可以看出创建线程的开销占整个时间的比例较大。
程序中需要注意的是,我们主要使用的线程池就是ThreadPoolExecutor, 此外还有定时线程池ScheduledThreadPoolExecutor。需要注意的是对于Executors.newCachedThreadPool()方法返回的线程池的使用,该方法没有线程的上限的,在使用时一定要当心,因为没有办法控制总体的线程数量,而每个线程都是需要消耗内从的,这可能会导致过多的内存被占用。建议尽量不要用这个方法返回线程池,而要使用有固定线程上限的线程池。
注:本文学习笔记总结