线程池化技术,是通过一个公用的线程队列,将一个或多个线程进行统一资源调用,可以支持线程资源的重复使用的技术,可以有效的避免因为线程调用创建和销毁线程过程带来的资源消耗问题。
为什么要使用线程池?
1、可以通过重复利用创建好的线程来降低线程创建和销毁造成的资源消耗
2、可以提升系统响应速度,因为核心线程是一直存在的,所以多线程任务不需要等待线程创建就可以立即执行任务。
3、可以利用线程池对线程资源进行统一的管理,避免线程创建过多导致的内存溢出问题等问题。
JAVA中如何来实现线程池的管理的?
Java是从JDK1.5开始的时候,将工作单元与线程执行机制进行了分离来提供线程池的使用管理机制,其中工作单元主要就是包括Runnable和Callable,而线程的执行机制则是交给了executors来提供,代码如下。
首先我们来创建一个用于测试的线程类。
public class TestThread implements Runnable {private String count;public TestThread(String count) {this.count = count;@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+" 开始计数 Count = "+count);executeCount();System.out.println(Thread.currentThread().getName()+" 结束计数");private void executeCount() {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();@Overridepublic String toString(){return this.count;
创建测试主类
public class SimpleTheadPool {public static void mAIn(String[] args) {// 创建线程池ExecutorService executorService = Executors.newFixedThreadPool(5);//创建执行线程操作for (int i = 0; i < 10; i++) {Runnable testThread = new TestThread(String.valueOf(i));executorService.execute(testThread);executorService.shutdown();while (executorService.isTerminated()){System.out.println("完成所有线程");
在测试类中,创建了一个固定大小的线程池,并且给线程池分配了10个需要执行的测试任务,因为线程池的初始大小是5,所以无法一次容纳10个线程的处理,所以当线程进入之后,会有一部分线程进入到等待状态,当前面进入的线程执行完成之后,后续的线程就会自动进入执行,执行效果如下图所示。
从执行结果来看,线程池中只存在了五个线程,并且这五个线程并不会随着工作执行完成而销毁,会一直等待分配线程执行任务,一直到线程池调用了销毁函数来进行销毁。
Executors使用了ExecutorService提供的线程实现,并且newFixedThreadPool() 方法创建了如下的一个线程池类。
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue());
下面我们就来看看ThreadPoolExecutor线程池类
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {// 省略代码
corePoolSize:核心线程数,当我们往线程池中添加一个任务之后,线程池会创建一个新的线程去执行任务,当创建的线程数等于corePoolSize之后。继续提交任务就会被阻塞到队列中,等待执行。当然如果调用了prestartAllCoreThreads()方法,线程池也会启动所有的核心线程。
workQueue:用来保存等待执行任务的阻塞队列。
maximumPoolSize:线程池中被允许的最大线程数。当等待执行阻塞队列装满之后,如果继续提交任务,则会继续创建线程去执行任务,前提是创建的线程数要小于maximumPoolSize值。这里需要注意的是如果队列可以存放的任务是无限的,那么这个参数就不会起作用,因为队列会一直存放等待线程。
keepAliveTime:线程空闲等待时间,当线程没有需要执行的任务的时候,就会在等待指定时间之后被销毁,默认情况下只会销毁超过corePoolSize数的线程。
unit:设置等待的线程时间单位
threadFactory:创建线程的工厂类。通过自定义的方式可以给每个线程创建一个具有识别性的线程名称,用于后续问题定位。
handler:线程拒绝策略,当队列满的时候,如果没有空闲线程执行任务,那么就需要拒绝策略的参与。
实现方式
newFixedThreadPool
当线程数达到了核心线程数之后,即使没有可以执行的任务,线程池也不会释放线程。并且这个线程池采用了一个无界的队列,也就是说阻塞队列永远不会达到饱和。
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue());
newSingleThreadExecutor
初始化线程池中只有一个线程执行任务,如果这个线程因为异常而结束工作,则会重新创建一个新的线程来继续执行该任务,这个唯一线程的执行操作就可以保证所有的提交任务都是按照队列排序顺序执行。
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue()));
newCachedThreadPool
这线程池的线程数可以达到Integer.MAX_VALUE值,并且内部使用了SynchronousQueue队列来作为阻塞队列。
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue());
总结
上面我们介绍了在Java中如何去创建一个线程池,并且介绍了创建线程池相关的核心参数。在后续的分享中还会给大家详细介绍关于Java中线程池的使用,敬请期待