问题1:Async在同一个类中注解失效,看如下代码
@SpringBootTest
@EnableAsync
public class ApplicationTest{
@Test
public void asyncTest(){
async2();
async1();
}
@Async
public void async1(){
System.out.println("say async1");
System.out.println("say async1 end");
}
@Async
public void async1(){
System.out.println("say async2");
try{
Thread.sleep(5000l);//暂停5秒
}catch(Exception e){
}
System.out.println("say async2 end");
}
}
运行代码后输入如下结果:
say async2
say async2 end
say async1
say async1 end
结果跟我们预期的不符,为什么呢?
原来Spring扫描注解后,会创建一个如下代理类(伪代码)
class proxy$ApplicationTest{
ApplicationTest aT = new ApplicationTest();
public void asyncTest(){
//由于asyncTest()没有异步注解,所以不会异步执行,而是直接调用ApplicationTest实例的asyncTest()方法
aT.asyncTest();
}
public void async1(){
//异步执行
aT.async1();
}
public void async2(){
//异步执行
aT.async2();
}
}
举一反三,@cacheable,@Scheduled以及@Transactional 是否也是相同原因
问题2 异步线程没执行完就停止了,请看如下代码:
@SpringBootTest
@EnableAsync
public class ApplicationTest{
@Autowired
private AsyncTest asyncTest;
@Test
public void asyncTest2() throws InterruptedException{
asyncTest.async1();
asyncTest.async2();
System.out.println("结束了");
}
}
@Service
public class AsyncTest {
@Async
public void async1(){
System.out.println("say async1");
System.out.println("say async1 end");
}
@Async
public void async2(){
System.out.println("say async2");
try{
Thread.sleep(1000l);
}catch (Exception e){
System.out.println("------sleep 1000-------");
}
System.out.println("say async2 end");
}
}
执行结果如下:
结束了
say async1
say async1 end
say async2
结果跟我们预期的不符,为什么呢?
其实junit是将test作为参数传递给了TestRunner的main函数。并通过main函数进行执行
public static void main(String[] args) {
TestRunner aTestRunner = new TestRunner();
try {
TestResult r = aTestRunner.start(args);
if (!r.wasSuccessful()) {
System.exit(1);
}
System.exit(0);
} catch (Exception var3) {
System.err.println(var3.getMessage());
System.exit(2);
}
}
在这里我们明显可以看到:当aTestRunner调用start方法后不会去等待子线程执行完毕在关闭主线程,
而是直接调用TestResult.wasSuccessful()方法,
不管返回true or false 都会结束当前运行的jvm虚拟机,所以使用junit测试多线程方法的结果异常就正常了;
解决方案如下:
/**
* 解决方案1
* 延长主线程执行时间
*/
@Test
public void asyncTest3() throws InterruptedException {
asyncServiceTest.async1();
asyncServiceTest.async2();
Thread.sleep(6000L);
System.out.println("异步threadId:" + Thread.currentThread().getId());
System.out.println("执行完毕!");
}
/**
* 解决方案2
* 等待异步线程执行完后再继续后续逻辑
*/
@Test
public void asyncTest() throws InterruptedException, ExecutionException {
Future task1 = asyncServiceTest.doTask1();
Future task2 = asyncServiceTest.doTask2();
while (true) {
if (task1.isDone() && task2.isDone()) {
System.out.println("Task1 result:" + task1.get());
System.out.println("Task2 result:" + task2.get());
break;
}
Thread.sleep(1000);
}
System.out.println("All tasks finished.");
}
@Service
public class AsyncServiceTest {
@Async
public Future doTask1() throws InterruptedException {
log.info("Task1 started.");
long start = System.currentTimeMillis();
Thread.sleep(5000);
long end = System.currentTimeMillis();
log.info("Task1 finished, time elapsed: {} ms.", end - start);
return new AsyncResult<>("Task1 accomplished!");
}
@Async
public Future doTask2() throws InterruptedException {
log.info("Task2 started.");
long start = System.currentTimeMillis();
Thread.sleep(3000);
long end = System.currentTimeMillis();
log.info("Task2 finished, time elapsed: {} ms.", end - start);
return new AsyncResult<>("Task2 accomplished!");
}
}
重试简单实现(retry)
@SpringBootTest
@EnableRetry
public class ApplicationTest3 {
@Autowired
private RetryServiceTest retryServiceTest;
@Test
public void retryTest3() {
System.out.println("--start test retry---");
retryServiceTest.retryTest3();
System.out.println("--end test retry---");
}
}
@Retryable(value = {Exception.class}, maxAttempts = 5, backoff = @Backoff(delay = 1000, multiplier = 1))
public void retryTest3() {
String method = "retryTest3";
log.info("[{}]do something...", method);
throw new RemoteAccessException("RemoteAccessException....");
}
--start test retry---
2018-04-17 10:02:05.706 INFO 7952 --- [ main] c.t.retry.servicetest.RetryServiceTest : [retryTest3]do something...
2018-04-17 10:02:06.708 INFO 7952 --- [ main] c.t.retry.servicetest.RetryServiceTest : [retryTest3]do something...
2018-04-17 10:02:07.709 INFO 7952 --- [ main] c.t.retry.servicetest.RetryServiceTest : [retryTest3]do something...
2018-04-17 10:02:08.709 INFO 7952 --- [ main] c.t.retry.servicetest.RetryServiceTest : [retryTest3]do something...
2018-04-17 10:02:09.709 INFO 7952 --- [ main] c.t.retry.servicetest.RetryServiceTest : [retryTest3]do something...
RemoteAccessException....
recover....
--end test retry---
异步注解跟重试注解组合使用
@SpringBootTest
@EnableRetry
@RunWith(SpringRunner.class)
@EnableAsync
@Slf4j
public class ApplicationTest4 {
@Autowired
private RetryServiceTest retryServiceTest;
@Test
public void retryTest() {
log.info("-----start retryTest------");
Future task = retryServiceTest.retryTest();
while (true) {
if (task.isDone()) {
log.info("---finish----");
break;
}
}
}
}
@Service
@Slf4j
public class RetryServiceTest {
@Async
@Retryable(value = {Exception.class}, maxAttempts = 5, backoff = @Backoff(delay = 1000, multiplier = 1))
public Future retryTest() {
String method = "retryTest";
log.info("[{}]do something...", method);
throw new RemoteAccessException("RemoteAccessException....");
}
@Recover
public void recover(Exception e) {
System.out.println(e.getMessage());
System.out.println("recover....");
}
}
若有错误或者需要优化的地方,请在下方留言