线程池的小小理解
线程池的作用是服用线程。因为很多应用请求都是短小和频繁的,所以如果使用线程,那么一个请求完了,就得销毁这个线程,而创建线程和销毁线程的消耗往往比线程本身消耗大,因此,使用一个线程池,管理一定数量的线程,如果超过阈值就强制让请求等待,直到获得一个线程来处理。直到没有请求最终才销毁线程。
总结:线程池的作用:
①减少创建和销毁线程的消耗,每个线程都可以多次工作(线程复用);
②可以随时调整线程数,防止消耗过多内存(管理线程);
③通过线程池,能有效的控制线程的最大并发数,提高系统资源利用率,同时避免过多的资源竞争,避免堵塞(控制最大并发数)。
线程的复用:
1、线程的生命周期:
新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocking)、结束(Zombie)。
新建(New):新建一个Thread,初始化线程基本信息,就像一个普通的对象。
就绪(Runnable):处于这个状态的线程没有被运行,只是表示可以运行了。
运行(Running):何时运行取决于JVM里线程调度器的调度,当线程获取CPU后就会运行run()方法。
之后根据线程调度,线程就在 就绪——运行——阻塞 这三个状态下切换,直到run()方法结束,或者停止线程,才会终止线程。
所以,要让线程复用就必须让线程保持在(就绪——运行——阻塞)这三个状态。
java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类
ThreadPoolExecutor继承了AbstractExecutorService类,一共提供了4个构造函数,下面对各参数介绍一下:
corePoolSize:核心池的大小。在创建线程池后,默认情况下线程池里是没有线程的,等待有任务过来了,才会创建线程去执行任务,当线程数达到corePoolSize后,就把任务放在缓存队列中等待线程空闲再执行。如果调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。
maximumPoolSize:线程池最大线程数。表示一个线程池最多能创建的线程数。
keepAliveTime:表示如果一个线程没有任务执行最多保持多久到达终止状态。默认情况下,只有在线程池中的线程数大于corePoolSize时,这个参数才会对空闲的那个线程起作用。如果设置了allowCoreThreadTimeOut(boolean)方法,那么在线程数不大于corePoolSize时,这个参数也能起作用,知道线程数为0。
unit:keepAliveTime的时间单位。
workQueue:阻塞队列,用来存储等待执行的任务。这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般使用LinkedBlockingQueue和SynchronousQueue。(几种阻塞队列策略在这篇博客里写的比较详细 http://blog.csdn.net/zhongxiangbo/article/details/70882342)。
用一个流程来解释下:
1)当池子大小小于corePoolSize就新建线程,并处理请求
2)当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去从workQueue中取任务并处理
3)当workQueue放不下新入的任务时,新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize就用RejectedExecutionHandler来做拒绝处理
4)另外,当池子的线程数大于corePoolSize的时候,多余的线程会等待keepAliveTime长的时间,如果无请求可处理就自行销毁。
ThreadPoolExecutor继承了AbstractExecutorService(抽象类),AbstractExecutorService实现了ExecutorService(接口),ExecutorService继承了Executor(接口)。
本文参照了博客,写的很好:http://blog.luoyuanhang.com/2017/02/26/thread-pool-in-java-1/

浙公网安备 33010602011771号