场景,有两个接口分别会接收两个数据,如果其中一个数据校验失败,那么另一个数据也要删掉。
由于是两个独立的数据源,没法做事务的回滚。( 这两个数据源是来自canal的监听得到的)。
因此考虑的方案就是,当校验不通过的时候,通过异步的方式,去起一个线程去删掉另一个接口过来的任务,但是这两个数据过来会有延时几秒,因此在另一个线程里面,先休眠3秒在执行删除。
因此考虑用一个线程池,防止校验不通过的数据过多导致创建过多的线程让系统出现问题。
线程池的参数:
1.第一个参数是核心线程数 2个
2.第二个参数是最大线程数10个
3.第三个参数是空闲线程存活时间60秒
4.第四个参数是时间单位 秒
5.线程任务队列,设置成了100个任务, 有界队列,如果队列满了,就会创建新的线程 ,最大不超过10个。 如果还有任务,就会执行拒绝策略
6.线程工厂,继承了ThreadFactor ,用于创建线程,设置了线程名称 ,方便后续出问题排查
7.拒绝策略,这里的策略是直接放弃任务,并打印日志。
new ThreadPoolExecutor(2, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(100),
(Runnable r) -> {
Thread t = new Thread(r);
t.setName("删除校验失败的对帐单关联的保险数据线程");
log.info("启动"+t.getName());
return new Thread(r);
}, (Runnable r, ThreadPoolExecutor executor) -> {
log.error("已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了");
});
拒绝策略,默认的几种拒绝策略有 1.直接抛异常,2.忽视掉,3.丢掉最老的任务,4.让当初线程执行
这里我们选择忽视掉并打印日志。
得用单例模式,保证线程池的唯一,不至于每一次都创建一个新的线程池。不然这个线程池就没意义了。
懒汉模式,通过静态内部类实现,因类只要用的时候才会被加载,且只加载一次,且加载过程是加锁的(jvm实现)
public class CheckAccountThreadPoolUtil {
//静态方法获取对象
public static ExecutorService getWorkPoolUtil() {
//调用内部静态类的静态方法获取对象
//这个时候触发内部类的加载 也就是只有在用的时候才会加载,懒汉模式
return Helper.getInstance();
}
//内部类加载之后
private static class Helper {
//静态字段 在类加载的时候被初始化 且只初始化一次
private static ExecutorService instance=new ExecutorService(){}.... ;
//获取对象
public static ExecutorService getInstance() {
return instance;
}
}
单例和非单例的验证
非单例
public class CheckAccountThreadPoolUtil {
public static ExecutorService getWorkPoolUtil() {
return Helper.getInstance();
}
private static class Helper {
private static ExecutorService instance;
public static ExecutorService getInstance() {
if (instance!=null){
return instance;
}
instance = new ThreadPoolExecutor(2, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(100),
(Runnable r) -> {
Thread t = new Thread(r);
t.setName("删除校验失败的对帐单关联的保险数据线程");
log.info("启动"+t.getName());
return new Thread(r);
}, (Runnable r, ThreadPoolExecutor executor) -> {
log.error("已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了");
});
return instance;
}
}
}
十个线程同时获取如下
public static void main(String[] args) throws InterruptedException, IOException {
//同时启动10个 看是否是单例
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
log.info("进入等待");
countDownLatch.countDown();
try {
//在这个节点等待countDownLatch减少到0
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("等待结束");
System.out.println("executorService:" + CheckAccountThreadPoolUtil.getWorkPoolUtil().hashCode());
}).start();
System.out.println("启动线程数"+(i+1));
}
}
结果
启动线程数1
启动线程数2
启动线程数3
启动线程数4
启动线程数5
启动线程数6
启动线程数7
启动线程数8
启动线程数9
启动线程数10
00:05:19.932 [Thread-2] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.932 [Thread-6] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.933 [Thread-1] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.933 [Thread-4] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.932 [Thread-7] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.932 [Thread-8] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.933 [Thread-3] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.932 [Thread-5] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.933 [Thread-0] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.933 [Thread-9] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.936 [Thread-9] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-7] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-4] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-2] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-1] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-8] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-3] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-5] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-6] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-0] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:1188407569
executorService:1730989841
executorService:1748713658
executorService:693173612
executorService:2005060830
executorService:1390979302
executorService:2082070238
executorService:1303637660
executorService:2098748826
executorService:1338817235
可以看到,每个hashcode都不一样 。
改成单例之后如下
public class CheckAccountThreadPoolUtil {
public static ExecutorService getWorkPoolUtil() {
return Helper.getInstance();
}
private static class Helper {
private static ExecutorService instance =
new ThreadPoolExecutor(2, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(100),
(Runnable r) -> {
Thread t = new Thread(r);
t.setName("删除校验失败的对帐单关联的保险数据线程");
log.info("启动"+t.getName());
return new Thread(r);
}, (Runnable r, ThreadPoolExecutor executor) -> {
log.error("已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了");
});
public static ExecutorService getInstance() {
return instance;
}
}
}
测试验证代码不变 ,结果如下:
启动线程数1
启动线程数2
启动线程数3
启动线程数4
启动线程数5
启动线程数6
启动线程数7
启动线程数8
启动线程数9
启动线程数10
00:11:20.293 [Thread-3] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-4] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-9] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-0] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-2] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-1] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-7] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-5] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-6] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-8] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.298 [Thread-8] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-4] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-0] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-1] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-9] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-5] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-7] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-6] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-2] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-3] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
package com.souche.sfs.server.common.util;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.concurrent.*;
/**
* @ClassName CheckAccountThreadPoolUtil
* @Author laixiaoxing
* @Date 2019/5/8 下午4:45
* @Description 用来异步删掉校验失败的对账单对应的保险数据的线程池
* @Version 1.0
*/
@Slf4j
public class CheckAccountThreadPoolUtil {
public static ExecutorService getWorkPoolUtil() {
return Helper.getInstance();
}
private static class Helper {
private static ExecutorService instance=
new ThreadPoolExecutor(2, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(100),
(Runnable r) -> {
Thread t = new Thread(r);
t.setName("删除校验失败的对帐单关联的保险数据线程");
log.info("启动"+t.getName());
return new Thread(r);
}, (Runnable r, ThreadPoolExecutor executor) -> {
log.error("已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了");
});
public static ExecutorService getInstance() {
return instance;
}
}
//测试
public static void main(String[] args) throws InterruptedException, IOException {
//挨个获取多个 看是否是单例
System.out.println("顺序启动获取");
ExecutorService executorService = CheckAccountThreadPoolUtil.getWorkPoolUtil();
System.out.println(executorService.hashCode());
ExecutorService executorService2 = CheckAccountThreadPoolUtil.getWorkPoolUtil();
System.out.println(executorService2.hashCode());
//同时启动10个 看是否是单例
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
log.info("进入等待");
//计数器减1
countDownLatch.countDown();
try {
//在这个节点等待countDownLatch减少到0
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("等待结束");
System.out.println("executorService:" + CheckAccountThreadPoolUtil.getWorkPoolUtil().hashCode());
}).start();
System.out.println("启动线程数"+(i+1));
}
//启动一批线程放进去
for (int i = 0; i < 200; i++) {
executorService.submit(()->{
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
System.in.read();
}
}
测试拒绝策略如下
顺序启动获取
314337396
314337396
启动线程数1
启动线程数2
启动线程数3
启动线程数4
启动线程数5
启动线程数6
启动线程数7
启动线程数8
启动线程数9
启动线程数10
23:25:40.656 [Thread-7] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.656 [Thread-3] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.655 [Thread-1] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.656 [Thread-9] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.656 [Thread-2] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.656 [Thread-8] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.656 [Thread-5] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.659 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.655 [Thread-0] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.655 [Thread-6] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.656 [Thread-4] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.661 [Thread-9] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
23:25:40.661 [Thread-7] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
executorService:314337396
23:25:40.661 [Thread-5] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
23:25:40.661 [Thread-4] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
23:25:40.661 [Thread-2] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
23:25:40.661 [Thread-3] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
23:25:40.661 [Thread-8] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
23:25:40.661 [Thread-1] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
23:25:40.661 [Thread-0] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
23:25:40.661 [Thread-6] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
executorService:314337396
23:25:40.660 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.663 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.664 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.664 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.664 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.665 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.665 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.665 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.665 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.665 [main] ERROR com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了
23:25:40.665 [main] ERROR com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了
23:25:40.665 [main] ERROR com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了
23:25:40.665 [main] ERROR com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了
…