- 什么是线程池?
在面向对象编程中,在创建和销毁线程是很费时间的,因为创建一个对象要获取内存资源或者其他更多的资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是很耗费资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些池化技术产生的原因。比如我们熟悉的数据库连接池,正是遵守这一思想而产生的。
Java线程池实现了一个Java高并发的、Java多线程的、可管理的统一调度器。
2,Executors工具类
Executors是个线程的工厂类,方便快速的创建很多线程池,也就是说一个线程池的工具类。配置一个线程池是比较复杂的,尤其是在对线程池的原理不清楚的情况下,很有可能配置的线程池不是最优的,因此,在Executors类里面提供了一些静态工厂,生成一些常用的线程池。常用的方法有一下三种:
- newSingleThreadExecutor:创建一个单线程的线程池;
- newFixedThreadPool: 创建国定大小的线程池。
- newCachedThreadPool:创建一个可缓存的线程池。
(1)newSingleThreadExecutor的使用
创建单个线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来代替它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
SingleThreadExecutor是使用单个worker线程的Executor。特点是使用单个工作线程执行任务。它的构造源码如下:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
执行过程如下:
1.如果当前工作中的线程数量少于corePool的数量,就创建一个新的线程来执行任务。
2.当线程池的工作中的线程数量达到了corePool,则将任务加入LinkedBlockingQueue。
3.线程执行完1中的任务后会从队列中去任务。
注意:由于在线程池中只有一个工作线程,所以任务可以按照添加顺序执行。
(2)newCachedThreadExecutor的使用
创建一个缓存池大小的可根据需要伸缩的线程池,但是以前构造的线程可用时将重用它,对于执行很多短期异步任务的程序而言,这些线程池的通性可提高程序的性能。调用execute将重用以前构造的线程。如果线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有60s未被使用的线程。因此长时间空闲线程不会使用任何资源。
CachedThreadPool是一个”无限“容量的线程池,它会根据需要创建新线程。特点是可以根据需要来创建新的线程执行任务,没有特定的corePool。下面是它的构造方法:
CachedThreadPool使用没有容量的SynchronousQueue作为主线程池的工作队列,它是一个没有容量的阻塞队列。每个插入操作必须等待另一个线程的对应移除操作。这意味着,如果主线程提交任务的速度高于线程池中处理任务的速度时,CachedThreadPool会不断创建新线程。极端情况下,CachedThreadPool会因为创建过多线程而耗尽CPU资源。其运行图如下:
执行过程如下:1.首先执行SynchronousQueue.offer(Runnable task)。如果在当前的线程池中有空闲的线程正在执行SynchronousQueue.poll(),那么主线程执行的offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行。,execute()方法执行成功,否则执行步骤2
2.当线程池为空(初始maximumPool为空)或没有空闲线程时,配对失败,将没有线程执行SynchronousQueue.poll操作。这种情况下,线程池会创建一个新的线程执行任务。
3.在创建完新的线程以后,将会执行poll操作。当步骤2的线程执行完成后,将等待60秒,如果此时主线程提交了一个新任务,那么这个空闲线程将执行新任务,否则被回收。因此长时间不提交任务的CachedThreadPool不会占用系统资源。
SynchronousQueue是一个不存储元素阻塞队列,每次要进行offer操作时必须等待poll操作,否则不能继续添加元素。
(3)FixedThreadPool的使用
创建固定长度的线程池,每次提交任务创建一个线程,直到达到线程池的最大数量,线程池的大小不再变化。
这个线程池可以创建固定线程数的线程池。特点就是可以重用固定数量线程的线程池。
FixedThreadPool运行图如下
执行过程如下:
1.如果当前工作中的线程数量少于corePool的数量,就创建新的线程来执行任务。
2.当线程池的工作中的线程数量达到了corePool,则将任务加入LinkedBlockingQueue。
3.线程执行完1中的任务后会从队列中去任务。
注意LinkedBlockingQueue是无界队列,所以可以一直添加新任务到线程池。
详细内容参考:Java 线程池详解