简单的使用多线程查询
主要的逻辑处理是在util包下,web层和mapper是简单的显示和sql查询,service层则是添加多个sql执行任务。
下面是显用来显示方法执行的时间,但是暂时没有显示出方法名称,
之前在做多数据源的时候,在用到aop根据注解来切换数据源的方法中,有根据JoinPoint参数来得到一个方法名称,之后可以考虑添加一下。
public class MethodTimer {
private static ThreadLocal<Long> time = new ThreadLocal<>();
private static final Logger LOGGER = LoggerFactory.getLogger(MethodTimer.class);
public static void start(){
time.set(System.currentTimeMillis());
}
public static void end(){
if(null != time.get()){
LOGGER.info("方法执行时长:"+(System.currentTimeMillis() - time.get()));
}else{
LOGGER.info("请先调用start()方法");
}
}
}
这是一个简单的接口,里面只有一个方法,就是用来在实现这个接口中的invoke方法里重写,具体的方法体里面加的是mapper层查询的语句,这个在后面的service里会有体现。
public interface Invoke {
public String invoke();
}
public class Worker implements Runnable {
private String name;
private CountDownLatch latch;
private Invoke invoke;
public String result;
public String getName() {
return name;
}
public String getResult() {
return result;
}
public Worker(String name, CountDownLatch latch, Invoke invoke) {
this.name = name;
this.latch = latch;
this.invoke = invoke;
}
@Override
public void run() {
try{
MethodTimer.start();
result = invoke.invoke();
} catch (Exception e) {
e.printStackTrace();
}finally {
latch.countDown();
MethodTimer.end();
}
}
}
具体解释看注释
public class Worker implements Runnable {
/**
* 这里我起的名是sql查询的结果,比如:tjxx-tpfl(统计信息-图片分类)
*/
private String name;
/**
* 了解的不多,暂时知道是计数器,在执行多线程查询的时候,任务的数量对应计数器的最大值,
* 只有计数器归0的时候,会在查询的主方法中结束
*/
private CountDownLatch latch;
/**
* 就是用来执行sql任务的,实现了Runnable接口,所有将invoke方法放在了run方法里面
*/
private Invoke invoke;
/**
* 用来存放sql查询的结果,类型不一定是String,根据需要变化。
*/
public String result;
public String getName() {
return name;
}
public String getResult() {
return result;
}
public Worker(String name, CountDownLatch latch, Invoke invoke) {
this.name = name;
this.latch = latch;
this.invoke = invoke;
}
@Override
public void run() {
try{
MethodTimer.start();
result = invoke.invoke();
} catch (Exception e) {
e.printStackTrace();
}finally {
/**
* 计数器需要减一 别忘了
*/
latch.countDown();
MethodTimer.end();
}
}
}
这里创建线程是通过线程池来创建的,线程池部分还需要多去了解
public class ThreadPoolUtil {
private static ExecutorService service;
static{
service = new ThreadPoolExecutor(5,8,2, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
public static void calcaulate(Runnable r){
service.execute(r);
}
}
主要的部分处理
public class ThreadManager {
/**
* 因为是线程查询,所以会有多个查询结果,所以用集合接
*/
private Map<String,String> result = new HashMap<>();
/**
* 多线程查询对应的多个执行任务,都是放在每一个invoke方法里面的
*/
private static Map<String,Invoke> invokes = Collections.synchronizedMap(new HashMap<String,Invoke>());
/**
* 计数器
*/
private CountDownLatch latch = null;
/**
* 添加执行任务的方法,也就是添加需要查询的sql
* @param name
* @param invoke
*/
public static void add(String name,Invoke invoke){
invokes.put(name,invoke);
}
/**
* 主要的处理
* @return
*/
public Map<String,String> calculate(){
try{
MethodTimer.start();
List<Worker> workers = new ArrayList<>();
//invoke里面存放的是执行任务的数量,所以这个数量就是计数器的开始值
latch = new CountDownLatch(invokes.size());
//遍历需要的执行sql任务
Set<Map.Entry<String,Invoke>> entrys = invokes.entrySet();
for(Map.Entry<String,Invoke> entry : entrys){
// 构建一个Woker对象,然后invoke里的sql查询任务就会在Worker对象中的run方法中执行
Worker w = new Worker(entry.getKey(),latch,entry.getValue());
workers.add(w);
// 执行sql任务,查询的结果会赋值给Woker对象的result属性
ThreadPoolUtil.calcaulate(w);
}
// 在计数器没有归0的时候会一直阻塞
latch.await();
for(Worker worker : workers){
// 因为每一个woker中的run方法都会将sql的查询结果赋值给result,所以这边将结果添加进result集合里面
result.put(worker.getName(),worker.getResult());
}
MethodTimer.end();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
下面就是service处理的部分,就是通过ThreadManager对象的add方法,添加每一个执行任务,执行任务就是通过实现Invoke的invoke方法执行的。
@Service
public class MultipleNumService {
@Autowired
private MultipleNumMapper mapper;
public Map<String,String> calcaulate(){
ThreadManager manager = new ThreadManager();
manager.add("第一个", new Invoke() {
@Override
public String invoke() {
return mapper.queryNum1();
}
});
manager.add("第二个", new Invoke() {
@Override
public String invoke() {
return mapper.queryNum2();
}
});
manager.add("第三个", new Invoke() {
@Override
public String invoke() {
return mapper.queryNum3();
}
});
return manager.calculate();
}
}
有个小插曲,就是在service层中注入mapper对象时,一直提示No bean…什么的,我在启动类中加了MapperScan注解,在mapper类中也加了mapper注解,但是最后,百度一下,使用的解决办法是在idea中将这个提示改为waring
file ----> settings—>Editor---->Inspections ----->spring----->
spring core ----> code -------> AutoWiring for Bean class 等级改为warning。
yl,wxhn.