Spring中@Async注解實現“方法”的異步調用


簡單介紹:

Spring為任務調度與異步方法執行提供了注解支持。通過在方法上設置@Async注解,可使得方法被異步調用。也就是說調用者會在調用時立即返回,而被調用方法的實際執行是交給Spring的TaskExecutor來完成。

開啟@Async注解:

<task:annotation-driven executor="annotationExecutor" />
<!-- 支持 @Async 注解 -->
<task:executor id="annotationExecutor" pool-size="20"/>

同時加入<context:component-scan />掃描注解。

栗子:

為了比較,先來一個同步調用:

@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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM