Java通过newCachedThreadPool和newFixedThreadPool创建的线程池详解

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就执行,所以比第三个线程先在控制台上打印。

猜你喜欢

转载自blog.csdn.net/deronn/article/details/80254397