public static ExecutorService newCachedThreadPool()创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程(如果新建了一个newCachedThreadPool类型的线程池结束任务后未调用shutdown()方法或者shutdownNow()方法,程序运行完后将在60秒后JVM进程退出)。因此,长时间保持空闲的线程池不会使用任何资源。
public static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在(必须调用shutdown方法退出JVM进程以退出程序)。新的线程加入后,如果正在运行的线程达到了上限,则会阻塞,直到有了空闲的线程来运行。
newFixedThreadPool线程池的实现过程:
/** * 线程池newFixedThreadPool的使用。 * */ public class ExecutorTest { public static void main(String args[]) { Random random = new Random(); // 建立一个容量为5的固定尺寸的线程池 ExecutorService executor = Executors.newFixedThreadPool(5); // 判断可是线程池可以结束 int waitTime = 500; for (int i = 0; i < 10; i++) { String name = "线程" + i; int time = random.nextInt(1000); waitTime += time; Runnable runner = new ExecutorThread(name, time); System.out.println("增加: " + name + " / " + time); executor.execute(runner); } try { Thread.sleep(waitTime); executor.shutdown(); executor.awaitTermination(waitTime, TimeUnit.MILLISECONDS); } catch (InterruptedException ignored) { } } } class ExecutorThread implements Runnable { private final String name; private final int delay; public ExecutorThread(String name, int delay) { this.name = name; this.delay = delay; } public void run() { System.out.println("启动: " + name); try { Thread.sleep(delay); } catch (InterruptedException ignored) { } System.out.println("完成: " + name); } }
因为以上代码随机生成了一个随机时间作线程延迟时间,其中一个输出结果如下:
增加: 线程0 / 13 增加: 线程1 / 161 启动: 线程0 增加: 线程2 / 685 启动: 线程1 增加: 线程3 / 400 启动: 线程2 增加: 线程4 / 444 启动: 线程3 增加: 线程5 / 349 启动: 线程4 增加: 线程6 / 37 增加: 线程7 / 740 增加: 线程8 / 595 增加: 线程9 / 133 完成: 线程0 启动: 线程5 完成: 线程1 启动: 线程6 完成: 线程6 启动: 线程7 完成: 线程5 启动: 线程8 完成: 线程3 启动: 线程9 完成: 线程4 完成: 线程9 完成: 线程2 完成: 线程7 完成: 线程8通过循环算法向一个长度为5的定长线程池添加线程,在一般的情况下是先从线程0,线程1,线程2,线程3,线程4顺序添加进入线程池里面(有时候不按照这个顺序添加,可能是JVM的机制的问题),添加完一个后立即开启线程,在添加完10个线程后,在前5个添加的线程中延迟时间最小的线程最先完成(线程0的延迟执行时间为13ms最先完成),在执行完一个线程后,在等待队列的某一个线程获的时间片立即启动(在工作中的线程数量固定为5),直至所有线程完成任务,在通过shutdown停止线程池(停止的时间应该是在完成了所有的线程任务的情况再停止的)。
newCachedThreadPool实现线程重用:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println("第1次执行线程"); System.out.println("线程名:"+Thread.currentThread().getName()); } }); //cachedThreadPool.shutdownNow(); try { Thread.sleep( 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println("第2次执行线程"); System.out.println("线程名:"+Thread.currentThread().getName()); } }); try { Thread.sleep( 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println("第3次执行线程"); System.out.println("线程名:"+Thread.currentThread().getName()); } }); try { Thread.sleep( 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println("第4次执行线程"); System.out.println("线程名:"+Thread.currentThread().getName()); } }); cachedThreadPool.shutdown();
控制台打印结果为:
第1次执行线程 线程名:pool-1-thread-1 第2次执行线程 线程名:pool-1-thread-1 第3次执行线程 线程名:pool-1-thread-1 第4次执行线程 线程名:pool-1-thread-1
线程池每次执行线程时都是相同的线程,可见线程池没有每次新建新的线程执行任务,在某个线程执行完了任务后线程池对线程进行了复用,减少对象创建和消亡的时间,提高性能。
重用的线程必须是已经完成了原来的任务的线程才能被重用:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println("第1次执行线程"); System.out.println("线程名:"+Thread.currentThread().getName()); } }); //cachedThreadPool.shutdownNow(); try { Thread.sleep( 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println("第2次执行线程"); System.out.println("线程名:"+Thread.currentThread().getName()); } }); try { Thread.sleep( 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(2000); //设置线程延迟2秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("第3次执行线程"); System.out.println("线程名:"+Thread.currentThread().getName()); } }); try { Thread.sleep( 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println("第4次执行线程"); System.out.println("线程名:"+Thread.currentThread().getName()); } }); cachedThreadPool.shutdown();
输出结果为:
第1次执行线程 线程名:pool-1-thread-1 第2次执行线程 线程名:pool-1-thread-1 第4次执行线程 线程名:pool-1-thread-2 第3次执行线程 线程名:pool-1-thread-1第4次执行时pool-1-thread-1还没有完成线程任务,所以线程池创建了一个pool-1-thread-2,而且这个线程在主线程只延时了1s就执行,所以比第三个线程先在控制台上打印。