看到一个需求:得到一个请求,然后获得数据后响应给前台,并且把得到的数据添加到数据库中,需要异步处理.如果同步的话,需要等待写入数据库,响应前台的时间会变长.
使用到了springmvc的@Async异步处理.
使用方法是在mvc配置文件中加入<task:annotation-driven />
头文件加入xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
然后在使用到的controller注入的业务类的方法上面,加上@Async
下面是我写的例子:
@Controller
public class Test {
@Autowired
private Service service;
@RequestMapping("/index")
public String index() throws InterruptedException {
service.go();
return "index";
}
}
@Component
public class Service {
@Async
public void go() throws InterruptedException {
for(int i=0;i<100000;i++) {
Thread.sleep(1000);
System.out.println("哈哈哈");
}
}
}
最终的效果是,接收请求后,代码会跳过异步业务直接响应到前台,异步业务会在前面业务执行完后自行后台执行.
那么我想到,如果需求是需要请求后台,后台同时处理多个业务,并且要将得到的数据响应到前台,如果单线程的话,时间会是所有处理时间的总和,等待响应时间比较长.
如果使用@Async这样异步处理的话,就不会等所有处理结果出来后,响应到前台.使用创建多个线程同时处理,多个线程也不会等全部执行完成后一起响应.
这时用到了CyclicBarrier类,它里面的await()方法,就起到了让多个线程互相等待,全部执行完成后再执行后面的代码.下面写的例子:
@Component
public class Service {
public String b() {
try {
Thread.sleep(5000); //模拟执行业务
} catch (InterruptedException e) {
e.printStackTrace();
}
return "bbbb";
}
public String a() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "aaa";
}
}
@Controller
public class Test {
@Autowired
private Service service;
@RequestMapping("/index")
public String index(Model model) {
//这里构造的参数,代表了几个线程进入屏障,进行等待
CyclicBarrier cyc = new CyclicBarrier(3);
Thread th1 = new Thread() {
@Override
public void run() {
String a = service.a();
model.addAttribute("a", a);
try {
cyc.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
};
Thread th2 = new Thread() {
@Override
public void run() {
String b = service.b();
model.addAttribute("b", b);
try {
cyc.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
};
long t1 = System.currentTimeMillis();//计算处理时间
th1.start();
th2.start();
try {
cyc.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
long t2 = System.currentTimeMillis();
model.addAttribute("c", t2 - t1);
return "index";
}
}
前台接收到的结果:
aaa
bbbb
5033 //这个时间就是其中一个需要处理最久的业务的时间
CyclicBarrier构造参数中设置的数量的所有的线程必须到齐后才能一起通过这个障碍,继续执行后面的代码.
如果使用CyclicBarrier(int parties,Runable barrierAction)这个构造,后面的实现Runable的代码,将在所有线程到齐后被执行.
CountDownLatch这个类的可以做到跟CyclicBarrier同样功能.
构造方法CountDownLatch(int count),参数是计数值.当用到方法countDown()时,计数减一.使用方法await()将挂起线程,等待计数器减到0后,继续执行其他线程.