只要调用了这两个关闭方法的其中一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于我们应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow。
三、线程池种类常见的五种线程池
newFixedThreadPool:固定数量线程池,这个比较常用.可以指定线程池的大小,该线程池corePoolSize和maximumPoolSize相等,阻塞队列使用的是LinkedBlockingQueue,大小为整数最大值
newSingleThreadExecutor: 单线程线程池,一般很少使用.
newCachedThreadPool:缓存线程池,缓存的线程默认存活60秒,线程的核心池corePoolSize大小为0,核心池最大为Integer.MAX_VALUE,阻塞队列使用的是SynchronousQueue。
newScheduledThreadPool:定时线程池,该线程池可用于周期性地去执行任务,通常用于周期性的同步数据。
newWorkStealingPool:工作窃取线程池,该线程为jdk1.8版新增。
1. newFixedThreadPoolpublic static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
这里思考一个问题为什么newFixedThreadPool的corePoolSize和mamximumPoolSize设计为一样的?
答案可以从execute的源码中找到,首先线程池提交任务时是先判断corePoolSize,再判断workQueue,最后判断mamximumPoolSize,然而LinkedBlockingQueue是无界队列,所以它是达不到判断mamximumPoolSize这一步的,所以mamximumPoolSize成多少,并没有多大所谓。
下边简单的介绍一下newFixedThreadPool的使用
public class ThreadPoolDemo { public static class MyTask implements Runnable { @Override public void run() { System.out.println(System.currentTimeMillis() + ":ThreadID:" + Thread.currentThread().getId()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { MyTask task = new MyTask(); ExecutorService es = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; ++i) { es.submit(task); } } }
这里创建了固定大小为5的线程,然后依次向线程池提交了10个任务。此后线程池就会安排调度这10个任务。每个任务都会将自己的执行时间和执行任务线程的Id打印出来,并且每一个任务执行时间为1秒
执行代码,输出如下
可以看出,前5个任务和后5个任务执行时间相差1秒,并且前五个和后五个ID是一致的。这说明任务是分两个批次执行。这也符合一个只有5个线程的线程池行为
2. newCachedThreadPool该方法返回一个可根据实际情况调整线程数量大小的线程池,线程池的数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程,如果所有线程均在工作,又有新的任务被提交,
则会创建新的线程执行任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
这里可以看到CachedThreadExecutor的mamximumPoolSize被设计成接近无限大。