自建线程池的参数介绍和spring启动类配置线程池执行定时任务
线程池中ThreadPoolExecutor构造器有7个参数,如下所示。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
一、线程池的作用
1.减少资源的开销
2.减少了每次创建线程、销毁线程的开销
3.提高了响应速度
每次请求到来时,由于线程的创建已经完成,故可以直接执行任务,因此提高了响应速度。
提高线程的可管理行,线程是一种稀缺资源,若不加以限制,不仅会占用大量资源,而且会影响系统的稳定性
因此,线程池可以对线程的创建和停止、线程数量等等因素加以控制,使得线程在一种可控的范围运行,不仅能保证系统稳定运行,而且方便性能调优。
二、自建线程池的参数介绍
1. corePoolSize
核心池大小corePoolSize:表示线程池维护线程的最少数量
2. maximumPoolSize
最大池大小maximumPoolSize:表示线程池维护线程的最大数量
3. workQueue
阻塞队列workQueue:表示如果任务数量超过核心池大小,多余的任务添加到阻塞队列中
4. corePoolSize、workQueue、maximumPoolSize的关系
前提假设:向线程池每添加一个任务就sleep。也就是说假设任务数与线程数一一对应,每添加一个任务就对应的创建一个线程,并且一直等待其他线程。
因为可能某一个线程执行了两个任务,看不出效果。
a.
if 任务数 <= 核心池大小
则每添加一个任务就会创建一个线程来执行该任务,线程最大数量等于核心池大小
b.
if 任务数 > 核心池大小 && 任务数 <= 核心池大小 + 阻塞队列大小
则线程数量等于核心池大小,其余任务放入到阻塞队列中
c.
if 任务数 > 核心池大小 + 阻塞队列大小 && 任务数 <= 最大池大小
则会创建新的线程来处理新的任务
d.
if 任务数 > 最大池大小
则会采用拒绝策略handler
5. 参数keepAliveTime
6. 参数unit
7. 参数threadFactory
8. 参数handler
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 线程池维护线程所允许的空闲时间的单位
handler: 线程池对拒绝任务的处理策略
Executor
9.实例
ExecutorService fixedThreadPool = new ThreadPoolExcutor(3,10,0LTimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
三、spring启动类配置线程池执行定时任务
1.启动类增肌@EnableAsync注解。可识别异步注解
2.启动类配置线程池,以支持异步执行定时任务
@Bean(name = "scheduledAsyncPoolExecutor")
public ThreadPoolTaskExecutor getAsyncPoolPoolTaskExecutor(){
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//线程池维护先吃的最少数量,定时任务通常同一时间执行的不会超过3个
taskExecutor.setCorePoolSize(3);
//线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
taskExecutor.setMaxPoolSize(30);
//缓冲队列,定时任务需要及时执行,不能进行缓存,否则获得锁会有问题
taskExecutor.setQueueCapacity(0);
//允许的空闲时间,当超过了核心线程数之外的线程在空闲时间达到之后会被销毁
taskExecutor.setKeepAliveSeconds(60);
//设置线程的前缀名
taskExecutor.setThreadNamePrefix("scheduled-");
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
taskExecutor.initialize();
return taskExecutor;
}
3.定时任务service类的代码
@Service
public class ScheduledScanningService {
//每天凌晨执行一次任务
@Scheduled(cron = "${query.loan.custInfo.result:0 0 4 * * ?}")
@Async("scheduledAsyncPoolExecutor")//异步执行任务,value值为配置的线程池
public void scheduledQueryLoanAndCustInfo() {
//项目部署至多台服务器上,使用redis锁,只需一台服务器执行即可
String lockId = "redis_lock_key";
//获得redis锁
Long canRun = getRedisLock(lockId, String.valueOf(System.currentTimeMillis()), "600");
String ip = getServiceIP();
//将redis锁标识,redis锁,拿到redis锁的时间,拿到redis锁服务器的ip记录至mongo日志中
addLogScheduledBymongolog(lockId, String.valueOf(canRun), System.currentTimeMillis(), ip);
}
四、Java中自带的线程池介绍
五、线程池的代码
private volatile ThreadPoolExecutor consumerPool;
//异步线程池
@PostConstruct
private void init(){
//最大线程数设置与CPU一致
//参数依次为:核心线程数、最大线程数、等待时间、时间单位、队列长度、策略
consumerPool = new ThreadPoolExecutor(1,4,5L,
TimeUnit.MINUTES,new LinkedBlockingDeque<>(5000*2),new ThreadPoolExecutor.DiscardPolicy());
//JVM之前销毁线程池
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
consumerPool.shutdown();
}
}));
}
五、CountDownLatch对异步线程进行收束
public static void main(String[] args) throws InterruptedException {
//设置线程个数
CountDownLatch latch = new CountDownLatch(3);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
latch.countDown();
}
latch.countDown();
System.out.println("第一个线程");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
//执行完后,减一
latch.countDown();
}
latch.countDown();
System.out.println("第二个线程");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
System.out.println("第三个线程");
} catch (InterruptedException e) {
//执行完后,减一
latch.countDown();
}
latch.countDown();
}
}).start();
//可进行线程的收束,异常线程完后减数,立刻唤起主线程执行
//否则最多阻塞2分钟,主线程执行
latch.await(2,TimeUnit.MINUTES);
System.out.println("结束。。。");
}