Spring @Async同步和非同步
阿新 • • 發佈:2018-11-30
Spring為任務排程與非同步方法執行提供了註解支援。通過在方法上設定@Async註解,可使得方法被非同步呼叫。也就是說呼叫者會在呼叫時立即返回,而被呼叫方法的實際執行是交給Spring的TaskExecutor來完成。
開啟@Async註解:
<task:annotation-driven executor="annotationExecutor" />
<!-- 支援 @Async 註解 -->
<task:executor id="annotationExecutor" pool-size="20"/>
同時加入掃描註解。
為了比較,先來一個同步呼叫
@Component
public class TestAsyncBean {
public void sayHello4() throws InterruptedException {
Thread.sleep(2 * 1000);//網路連線中 。。。訊息傳送中。。。
System.out.println("我愛你啊!");
}
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"classpath:/applicationContext.xml"}) public class TestAsync { @Test public void test_sayHello4() throws InterruptedException, ExecutionException { System.out.println("你不愛我了麼?"); testAsyncBean.sayHello4(); System.out.println("回的這麼慢, 你肯定不愛我了, 我們還是分手吧。。。"); Thread.sleep(3 * 1000);// 不讓主程序過早結束 } }
輸出結果:
你不愛我了麼?
我愛你啊!
回的這麼慢, 你肯定不愛我了, 我們還是分手吧。。。
同步呼叫會按程式碼順序依次進行下去,如果哪裡需要等待,那麼就阻塞在那裡,不再向下繼續進行。
使用@Async的非同步呼叫:
@Component public class TestAsyncBean { @Async public void sayHello3() throws InterruptedException { Thread.sleep(2 * 1000);//網路連線中 。。。訊息傳送中。。。 System.out.println("我愛你啊!"); } } @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"classpath:/applicationContext.xml"}) public class TestAsync { @Autowired private TestAsyncBean testAsyncBean; @Test public void test_sayHello3() throws InterruptedException, ExecutionException { System.out.println("你不愛我了麼?"); testAsyncBean.sayHello3(); System.out.println("你竟無話可說, 我們分手吧。。。"); Thread.sleep(3 * 1000);// 不讓主程序過早結束 } }
輸出結果:
你不愛我了麼?
你竟無話可說, 我們分手吧。。。
我愛你啊!
非同步呼叫,通過開啟新的執行緒來執行呼叫的方法,不影響主執行緒。非同步方法實際的執行交給了Spring的TaskExecutor來完成。
上面這種方式是沒有返回值的,下面嘗試有返回值的非同步呼叫:
@Component
public class TestAsyncBean {
@Async
public String sayHello2() throws InterruptedException {
Thread.sleep(2 * 1000);//網路連線中 。。。訊息傳送中。。。
return "我愛你啊!";// 呼叫方呼叫後會立即返回,所以返回null
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/applicationContext.xml"})
public class TestAsync {
@Autowired
private TestAsyncBean testAsyncBean;
@Test
public void test_sayHello2() throws InterruptedException, ExecutionException {
System.out.println("你不愛我了麼?");
System.out.println(testAsyncBean.sayHello2());
System.out.println("你說的啥? 我們還是分手吧。。。");
Thread.sleep(3 * 1000);// 不讓主程序過早結束
}
}
輸出結果
輸出結你不愛我了麼?
null
你說的啥? 我們還是分手吧。。。
接獲取返回值得方式是不行的,這裡就需要用到非同步回撥,非同步方法返回值必須為Future<>,就像Callable與Future。
下面通過AsyncResult<>來獲得非同步呼叫的返回值:
@Component
public class TestAsyncBean {
@Async
public Future<String> sayHello1() throws InterruptedException {
int thinking = 2;
Thread.sleep(thinking * 1000);//網路連線中 。。。訊息傳送中。。。
System.out.println("我愛你啊!");
return new AsyncResult<String>("傳送訊息用了"+thinking+"秒");
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/applicationContext.xml"})
public class TestAsync {
@Autowired
private TestAsyncBean testAsyncBean;
@Test
public void test_sayHello1() throws InterruptedException, ExecutionException {
Future<String> future = null;
System.out.println("你不愛我了麼?");
future = testAsyncBean.sayHello1();
System.out.println("你竟無話可說, 我們分手吧。。。");
Thread.sleep(3 * 1000);// 不讓主程序過早結束
System.out.println(future.get());
}
}
輸出結果:
你不愛我了麼?
你竟無話可說, 我們分手吧。。。
我愛你啊!
傳送訊息用了2秒
官方文件:
34.4.3 The @Async annotation