一、线程池定义
1.JDK中线程池类图
Executor:父接口,所有线程池都实现了这个接口,里面有一个excute()方法用于执行线程
ExecutorService:线程池接口,继承自Executor接口,供了生命周期管理的方法,返回 Future 对象,可以返回执行完的结果
ThreadPoolExecutor:线程池的具体实现类,一般使用ThreadPoolExecutor创建线程池
2.创建线程池的工具类
Executors:线程池的工具类,用于创建线程池,返回ExecutorService类型的线程池
1)public static ExecutorService newFiexedThreadPool(int Threads) :创建固定数目线程的线程池
2)public static ExecutorService newCachedThreadPool():创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果没有可用的线程,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程
3)public static ExecutorService newSingleThreadExecutor():创建一个单线程化的Executor
4)public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize):调度型线程池
3.区别
1)Executor 接口定义了 execute()
方法用来接收一个Runnable
接口的对象,不接受返回的对象,而 ExecutorService 接口中的 submit()
方法可以通过Future 对象接受Runnable
和Callable
接口的对象
2)ExecutorService 还提供用来控制线程池的方法。比如:调用 shutDown()
方法终止线程池
3)Executors 类提供工厂方法用来创建不同类型的线程池
4.线程池参数
corePoolSize:核心线程数
maximumPoolSize, //最大线程数
keepAliveTime:当线程数超过核心线程数,线程的最大存活时间
unit:keepAliveTime的时间单位
workQueue:阻塞队列
threadFactory: 创建线程的工厂
handler:拒绝策略
5.线程池执行顺序
1)当线程数小于 corePoolSize
时,创建线程执行任务。
2)当线程数大于等于 corePoolSize
并且 workQueue
没有满时,放入workQueue
中
3)线程数大于等于 corePoolSize
并且当 workQueue
满时,新任务新建线程运行,线程总数要小于 maximumPoolSize
4)当线程总数等于 maximumPoolSize
并且 workQueue
满了的时候执行 handler
的 rejectedExecution
。也就是拒绝策略
6.拒绝访问策略
ThreadPoolExecutor默认有四个拒绝策略:
1、ThreadPoolExecutor.AbortPolicy()
直接抛出异常RejectedExecutionException
2、ThreadPoolExecutor.CallerRunsPolicy()
直接调用run方法并且阻塞执行
3、ThreadPoolExecutor.DiscardPolicy()
直接丢弃后来的任务
4、ThreadPoolExecutor.DiscardOldestPolicy()
丢弃在队列中队首的任务
当然可以自己继承RejectedExecutionHandler来写拒绝策略.
二、线程池使用方法
1.Java JDK创建线程池的方法-ThreadPoolExecutor
1)使用ThreadPoolExecutor创建线程池
API如下所示:
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //当线程数超过核心线程数,线程的最大存活时间
TimeUnit unit, //keepAliveTime的时间单位
BlockingQueue<Runnable> workQueue) //阻塞队列容量
{
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
所在包:java.util.concurrent.*
2)代码实现:
3)结果展示:
2.Spring创建线程池方式-ThreadPoolTaskExecutor
所在包:package org.springframework.core.task;
创建线程池并注入spring容器,开启@EnableAsync注解方便后续使用它@Async异步化调用
@Configuration
@EnableAsync
public class BeanConfig {
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(5);
// 设置最大线程数
executor.setMaxPoolSize(10);
// 设置队列容量
executor.setQueueCapacity(20);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 设置默认线程名称
executor.setThreadNamePrefix("hello-");
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
3.异步调用线程池线程方法-ThreadPoolTaskExecutor
1)代码处使用
需要使用@Autowired装配获取注入的线程池对象
@Autowired
private ThreadPoolTaskExecutor executor;
/**
* 根据活动id获取指定活动,异步添加缓存操作
*/
@RequestMapping("/test9")
public String testTaskExecutor() throws Throwable {
log.info("进入方法:");
//异步调用
executor.execute(() -> {
try {
log.info("进入异步方法:");
Thread.sleep(2000);
jedisClusterClient.getJedisCluster().set("test9:async", "test");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("异步执行添加缓存:" + jedisClusterClient.getJedisCluster().get("test9:async"));
});
log.info("方法执行完毕");
return "test";
}
执行结果:
可以看到异步执行是在方法执行完毕后才执行的,不是同步的,异步化成功
2)使用@Async注解异步调用
注意:@Async修饰的方法的实例必须注入spring容器中方能使用,代码如下:
异步方法使用@Async调用:
执行结果:
可以看到结果也是异步化调用