Async&Retryable简单介绍

问题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<String> task1 = asyncServiceTest.doTask1();

        Future<String> 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<String> 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<String> 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<String> 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<String> 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....");

    }

    }

```

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容