线程池
线程池开启
随着请求任务的不断增加,并发的压力逐渐增大,JVM会开启更多的线程,会出现以下java api描述的几种情况:
如果正在运行的线程少于corePoolSize,Executor总是喜欢添加一个新线程去执行请求,而不是将任务(Runnable)排队,直到达到corePoolSize。
如果运行的线程等于或大于corePoolSize,Executor总是喜欢将,后来的新的请求(不是线程)加进队列排队等候,而不是添加一个新的线程。
如果请求数量并发继续增大,以至于队列满,请求无法排队,则会创建一个新线程去处理;假设创建线程后总线程数超出maximumPoolSize就不再创建线程,如果创建不了线程,任务将被拒绝,也即并发太高的情况。
线程池的开启方式:
Executors可以理解为一个线程池的工具类,它方法有很多有用的静态方法。
-
public static <T> Callable<T> callable(Runnable task, T result)
利用了一个RunnableAdapter的内部静态类,RunnableAdapter是一个适配器,利用了适配器模式。将一个Runnable接口封装为一个callable接口。
FutureTask的构造方法,
public FutureTask(Runnable runnable, V result)
就是一个将Runnable对象,封装成为Callable赋值给内部的Callable属性,进而完成对象的初始化。
-
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
可以创建ThreadPoolExecutor(继承了AbstractExecutorService 又继承了ExecutorService又继承了Executor),创建的方法有三种,但是底层调用的都是
public ThreadPoolExecutor(…) 构造器,构造器的参数说明:
int corePoolSize: 核心线程数量,池中要保持的线程数量,即使空闲,除非allowCoreThreadTimeOut参数被设置
int maximumPoolSize: 池中最大线程数量,当请求队列放满,核心线程不能及时处理新来的线程,线程工厂就继续开启线程直到达到maximumPoolSize
long keepAliveTime: 最大存活时间,当线程池中线程的数量大于corePoolSize,多余的空闲线程等待新任务的最大时间,如果超过keepAliveTime,线程对象就会被GC回收
TimeUnit unit:/时间单位
BlockingQueue workQueue: 同步队列,存放任务Runnable,这个队列只会存放execute提交的请求
ThreadFactory threadFactory: 线程工厂产生线程池
RejectedExecutionHandler handler:如果任务队列(BlockingQueue)排满并且线程超过最大容量(maximumPoolSize)时,新提交的任务会被拒绝(也即并发太高的情况),这时应该使用的处理器。
public ThreadPoolExecutor(
int corePoolSize,// 核心线程数量,池中要保持的线程数量,即使空闲,除非allowCoreThreadTimeOut被设置
int maximumPoolSize, // 池中最大线程数量,当请求队列放满,核心线程不能及时处理新来的线程,线程工厂就继续开启线程直到达到maximumPoolSize
long keepAliveTime,// 最大存活时间,当线程池中线程的数量大于corePoolSize,多余的空闲线程等待新任务的最大时间,如果超过keepAliveTime,线程对象就会被GC回收
TimeUnit unit,// 时间单位
BlockingQueue<Runnable> workQueue, // 同步队列,存放任务Runnable,这个队列只会存放execute提交的请求
ThreadFactory threadFactory,// 线程工厂产生线程池
RejectedExecutionHandler handler) {...}
Executors基本上共4种生成线程池服务ExecutorService的方式:
1.newFixedThreadPool() 2.newCachedThreadPool() 3.newSingleThreadScheduledExecutor() 4.newScheduledThreadPool()
底层调用的都是ThreadPoolExecutor的构造方法,不同只是BlockingQueue队列的不同。
- 自然切换构建无限大的线程池策略:使用的队列是SynchronousQueue,java api对它的描述是,一种默认比较好的切换任务的方式(对线程的任务),不用刻意的处理他们。内部机制:当没有足够的线程来处理任务时,将任务排入任务队列会失败,所以总是开启出新的线程来处理他们,当处理的任务有内部依赖关系时可以避免锁定。自然切换通常需要无限大的最大线程池数量maximumPoolSize,当有大量任务来临时,已有线程来不及处理,就有可能产生无限多的线程。
- Direct handoffs. A good default choice for a work queue is a {@link SynchronousQueue} that hands off tasks to threads without otherwise holding them. Here, an attempt to queue a task will fail if no threads are immediately available to run it, so a new thread will be constructed. This policy avoids lockups when handling sets of requests that might have internal dependencies. Direct handoffs generally require unbounded maximumPoolSizes to avoid rejection of new submitted tasks. This in turn admits the possibility of unbounded thread growth when commands continue to arrive on average faster than they can be processed.
- 构造无限大的队列策略:使用的队列是LinkedBlockingQueue,当所有的核心线程(corePoolSize)都忙时,构造一个没有预先定义大小的无界的队列,来存放新来的请求任务。所以比核心线程数量corePoolSize多的线程不会被开启,最大线程池数量maximumPoolSize参数也就不会生效。这种模式适用于,任务之间不能有相互依赖影响的关系,彼此之间独立的情况。这种情况适用于网页服务器和高并发请求的削峰。
- Unbounded queues. Using an unbounded queue (for example a {@link LinkedBlockingQueue} without a predefined capacity) will cause new tasks to wait in the queue when all corePoolSize threads are busy. Thus, no more than corePoolSize threads will ever be created. (And the value of the maximumPoolSize therefore doesn't have any effect.) This may be appropriate when each task is completely independent of others, so tasks cannot affect each others execution; for example, in a web page server.While this style of queuing can be useful in smoothing out transient bursts of requests, it admits the possibility of unbounded work queue growth when commands continue to arrive on average faster than they can be processed.
- 有界队列策略:使用ArrayBlockingQueue,制定最大线程池数量maximumPoolSize可以防止系统资源耗尽但是比较难以平衡和把控。一般使用大队列和小池子可以有效的减少cpu利用率,os资源,上下文切换量。但是可能会人为的导致并发吞吐量下降。如果有大量的任务阻塞(如io阻塞),系统可能会花费比你预计更多的时间。或者小队列大池子可以提高cpu利用率,但是可能会遇到过多不可接受的调度开销,这也会降低并发量。
- Bounded queues. A bounded queue (for example, an{@link ArrayBlockingQueue}) helps prevent resource exhaustion when used with finite maximumPoolSizes, but can be more difficult to tune and control. Queue sizes and maximum pool sizes may be traded off for each other: Using large queues and small pools minimizes CPU usage, OS resources, and context-switching overhead, but can lead to artificially low throughput. If tasks frequently block (for example if they are I/O bound), a system may be able to schedule time for more threads than you otherwise allow. Use of small queues generally requires larger pool sizes, which keeps CPUs busier but may encounter unacceptable scheduling overhead, which also decreases throughput.
拒绝的任务:当线程池服务关闭,或者当线程池使用有界的线程数量和任务队列(线程池达到最大容量,任务队列也满)。不论哪一种情况线程服务的execute方法都会调用RejectedExecutionHandler拒绝任务处理器的rejectedExecution方法。有几种已经定义好的处理策略:
AbortPolicy:抛出运行时异常
CallerRunsPolicy:调用execute自身的线程来执行新来的任务。这会降低新提交任务的速度。
DiscardPolicy:新来的任务会被抛弃
DiscardOldestPolicy:将队列中较早的任务抛弃。
Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程。使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor。而实际开发中任务一般是非阻碍的,即异步的,所以我们要在配置类中通过@EnableAsync开启对异步任务的支持,并通过在实际执行的Bean的方法中使用@Async注解声明其是一个异步任务。
此外,还提供一种Java的实现方式,多种方式去尝试如何去实现多线程。
一、基于Spring注解
定义配置类
import java.util.concurrent.Executor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
* @Description: 配置类实现AsyncConfigurer接口,并重写getAsyncExecutor方法,并返回一个ThreadPoolTaskExecutor,
* 这样我们就获得一个基于线程池TaskExecutor
* @ClassName: CustomMultiThreadingConfig
* @Author:
* @Date:
*/
@Configuration // 单例
@EnableAsync//利用@EnableAsync注解开启异步任务支持
public class CustomMultiThreadingConfig implements AsyncConfigurer{
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(25);
taskExecutor.initialize();
return taskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
}
}
创建线程任务
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* @Description: 创建线程任务服务
* @ClassName: CustomMultiThreadingService
* @Author:
* @Date:
*/
@Service
public class CustomMultiThreadingService {
private Logger logger = LoggerFactory.getLogger(CustomMultiThreadingService.class);
/**
* @Description:通过@Async注解表明该方法是一个异步方法,
* 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
* @Title:
* @Date:
* @Author:
* @Throws
* @param i
*/
@Async
public void executeAysncTask1(Integer i){
logger.info("CustomMultiThreadingService ==> executeAysncTask1 method: 执行异步任务{} ", i);
}
/**
* @Description:通过@Async注解表明该方法是一个异步方法,
* 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
* @Title: executeAsyncTask2
* @Date:
* @Author:
* @Throws
* @param i
*/
@Async
public void executeAsyncTask2(Integer i){
logger.info("CustomMultiThreadingService ==> executeAsyncTask2 method: 执行异步任务{} ", i);
}
}
调用线程任务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.only.mate.springboot.multithreading.CustomMultiThreadingService;
/**
* @Description:自定义多线程Controller
* @ClassName: CustomMultiThreadingController
* @Author:
* @Date:
*/
@Controller
@RequestMapping(value="/multithreading")
public class CustomMultiThreadingController {
@Autowired
private CustomMultiThreadingService customMultiThreadingService;
@ResponseBody
@RequestMapping(value="/dotask")
public String doTask() {
for (int i=0;i<10;i++){
customMultiThreadingService.executeAysncTask1(i);
customMultiThreadingService.executeAsyncTask2(i);
}
return "success";
}
}
二、基于Java线程实现
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @Description: 异步线程调度管理器
* @ClassName: CustomAsyncScheduler
* @Author:
* @Date:
*/
public class CustomAsyncScheduler {
private volatile static CustomAsyncScheduler instance;
private static ThreadPoolExecutor chnlBackendQueryPool;
private CustomAsyncScheduler() {
}
@SuppressWarnings({ "rawtypes", "static-access", "unchecked" })
public static CustomAsyncScheduler getInstance() {
if (instance == null) {// 自己实现单例
synchronized (CustomAsyncScheduler.class) {
if (instance == null) {
instance = new CustomAsyncScheduler();
BlockingQueue queue = new LinkedBlockingQueue();
chnlBackendQueryPool = new ThreadPoolExecutor(50, 100, 30, TimeUnit.SECONDS, queue);
chnlBackendQueryPool.allowCoreThreadTimeOut(true);
instance.setChnlBackendQueryPool(chnlBackendQueryPool);
}
}
}
return instance;
}
public ThreadPoolExecutor getChnlBackendQueryPool() {
return chnlBackendQueryPool;
}
public static void setChnlBackendQueryPool(ThreadPoolExecutor chnlBackendQueryPool) {
CustomAsyncScheduler.chnlBackendQueryPool = chnlBackendQueryPool;
}
public static void setInstance(CustomAsyncScheduler instance) {
CustomAsyncScheduler.instance = instance;
}
}
创建线程任务
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* @Description: 创建线程任务服务
* @ClassName: CustomMultiThreadingService
* @Author:
* @Date:
*/
@Service
public class CustomMultiThreadingService {
private Logger logger = LoggerFactory.getLogger(CustomMultiThreadingService.class);
/**
* @Description:通过@Async注解表明该方法是一个异步方法,
* 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
* @Title: executeAysncTask1
* @Date:
* @Author:
* @Throws
* @param i
*/
@Async
public void executeAysncTask1(Integer i){
logger.info("CustomMultiThreadingService ==> executeAysncTask1 method: 执行异步任务{} ", i);
}
/**
* @Description:通过@Async注解表明该方法是一个异步方法,
* 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
* @Title: executeAsyncTask2
* @Date:
* @Author:
* @Throws
* @param i
*/
@Async
public void executeAsyncTask2(Integer i){
logger.info("CustomMultiThreadingService ==> executeAsyncTask2 method: 执行异步任务{} ", i);
}
/**
* @Description: 异步线程调度管理器创建线程任务
* @Title: executeAsyncTask3
* @Date:
* @Author:
* @Throws
* @param i
*/
public void executeAsyncTask3(Integer i){
CustomAsyncScheduler.getInstance().getChnlBackendQueryPool().execute(new Runnable() {
@Override
public void run() {
logger.info("CustomMultiThreadingService ==> executeAsyncTask3 method: 执行异步任务{} ", i);
}
});
}
}
触发线程任务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.only.mate.springboot.multithreading.CustomMultiThreadingService;
/**
* @Description:自定义多线程Controller
* @ClassName: CustomMultiThreadingController
* @Author:
* @Date:
*/
@Controller
@RequestMapping(value="/multithreading")
public class CustomMultiThreadingController {
@Autowired
private CustomMultiThreadingService customMultiThreadingService;
@ResponseBody
@RequestMapping(value="/dotask")
public String doTask() {
for (int i=0;i<10;i++){
customMultiThreadingService.executeAysncTask1(i);
customMultiThreadingService.executeAsyncTask2(i);
}
return "success";
}
@ResponseBody
@RequestMapping(value="/dojob")
public String doJob() {
for (int i=0;i<10;i++){
customMultiThreadingService.executeAysncTask1(i);
customMultiThreadingService.executeAsyncTask2(i);
customMultiThreadingService.executeAsyncTask3(i);
}
return "success";
}
}
自己封装的一个线程池
package cm.soft.collect.util;
import cm.soft.collect.Config.LogConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Component
public class CustomThreadPool implements AutoCloseable {
static volatile CustomThreadPool customThreadPool;
@Autowired
LogConfig log;
private final ExecutorService threadPoolExecutor =
new ThreadPoolExecutor(5, 100, 30000,
TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(50000));
private CustomThreadPool() {
}
public static CustomThreadPool getPool() {
if (customThreadPool == null) {
synchronized (CustomThreadPool.class) {
if (customThreadPool == null) {
customThreadPool = new CustomThreadPool();
}
}
}
return customThreadPool;
}
public ExecutorService getService() {
return threadPoolExecutor;
}
@Override
@PreDestroy
public void close() {
if (threadPoolExecutor != null&&!threadPoolExecutor.isShutdown()) {
threadPoolExecutor.shutdown();
log.info("关闭线程池");
}
}
}