Java线程池的构造
Java线程池的类关系图
线程池的核心类 ThreadPoolExecutor
ThreadPoolExecutor: 所有的其他类和接口,都是围绕这个类提供的各自功能。
-
AtomicInteger ctl:记录线程池状态和线程池数量,选择32位来记录,高3位用来记录线程状态,低29位记录线程数量。111:running,000:shutdown,001:stop,010:tidying,011:terminated。
-
execute()方法:执行任务的方法。
主要是通过判断线程数量,来执行addWorker()方法。
判断线程池状态,增加到队列成功之后,再判断一次状态和移除任务成功之后执行reject()方法。如果线程池的数量为0的时候,执行addWorker()方法。
判断线程池状态,增加到队列失败之后,在判断addWorker()是否成功,如果返回false执行reject()方法。
-
addWorker()方法:将任务增加队列。
这个方法,先判断一些线程池的状态,然后再判断线程池数量,然后再对ctl加1。
接着执行new Worker()操作,然后加锁之后,判断线程池状态,增加到workers中,释放锁之后,启动线程,t.start(),Worker内部类中run()中调用了,runWorker()方法。
-
runWorker()方法,这里执行增加锁,增加状态的判断,一些前置操作,和一些后置操作。再通过判断getTask()操作,获取在队列中的任务,来执行。所以runWorker方法,就是执行当前任务,执行完了,再去获取队列中的任务执行。
-
getTask()方法,判断状态,判断是否需要判断超时,如果需要超时判断则执行workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS),不需要超时判断就执行workQueue.take()。
**总结:**这个就是ThreadPoolExecutor的执行的大概流程。与上一回自己手写的流程大概过程是一样的,但是Java的执行过程增加状态判断,锁操作,队列操作考虑的更加完善。
接口Executor,ExecutorService
接口Executor定义线程池的执行方法:
void execute(Runnable command)
接口ExecutorService定义了线程池方法:
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
<T> Future<T> submit(Callable<T> task);
抽象类AbstractExecutorService
主要是实现了一些基本通用的方法
内部类Worker
这个类就是任务执行了类,通过run()调用runWorker()方法来执行自身当前的任务,也可以取出线程池队列中的任务进行执行。
RejectExecutionHandler:拒绝策略接口
AbortPlicy:抛出异常方式拒绝
DiscardPolicy:直接丢弃
DiscardOldPolicy: 丢弃存活时间最长的任务
CallerRunsPolicy:谁提交谁执行
如果自定义拒绝策略则通过实现这个接口
Executors: 创建我们常用不同策略的线程池
算是一个线程池创建的工具类,创建4中不同功能的线程池。
newFixedThreadPool:固定大小的线程池,LinkedBlockingQueue的无界阻塞队列存放等待线程,任务不能及时处理,无限的堆积,可能导致OOM。
newCachedThreadPool: 没有现在数量,最大数量设置为Integer.MAX_VALUE,SynchronousQueue 是一个生产消费模式的阻塞队列,只要有任务就需要线程执行,如果线程任务比较耗时,又需要大量创建,会导致OOM。
newScheduledThreadPool: 创建一个可以指定时间周期的线程池,new ScheduledThreadPoolExecutor.DelayedWorkQueue(),这个也是无限大小的线程池Integer.MAX_VALUE。这个也是无界阻塞队列存放线程,也存在OOM的异常。
newSingleThreadExecutor:只有一个线程的线程池;LinkedBlockingQueue的无界阻塞队列存放等待线程
这些线程池都有可能出现OOM的异常。
三, 线程池的使用
为什么要是用线程池,因为使用了多线程,但是单独new Thread(),肯定会造成资源的浪费,通过线程池减少这种浪费。多线程是用来提高服务器并发处理能力,减少服务器平均处理时间,减少服务端的压力,缩短服务处理的时间。
涉及到了理论的知识,利特尔法则和阿姆达尔定律;利特尔法则就是增加服务,或是缩短周期。所以在实际的工作中通过,我可以将服务修改为并行处理。这样就缩短的服务返回的时间。
Executors这个类是Java提供的,但是这个类创建的线程不够精细化,也非常容易造成OOM风险,所以实际的工作中,可以通过new ThreadPoolExecutor(),也可以使用Spring的线程配置ThreadPoolTaskExecutor。new ThreadPoolExecutor
获取线程池监控信息
- 通过重写线程池方式来监控
public classs ThreadPoolMonitor extends ThreadPoolExecutor {
@Override
public void shutdown {
...
}
...
}
- 基于JVMTI方式来监控
JVMTI就是JVM tool interface.通过c++代码来实现JVM层面的性能分析,内存管理,线程分析等。
总结
当业务量很小的时候,不需要复杂的技术;但是大厂他们的业务体量庞大,并发数高,让原本可能就是一个简单的查询接口,需要做熔断,降级,限流,缓存,线程,异步,预热等等的操作。