Spring @Async實現異步調用示例

什麼是異步調用java

       「異步調用」對應的是「同步調用」,同步調用指程序按照定義順序依次執行,每一行程序都必須等待上一行程序執行完成以後才能執行;異步調用指程序在順序執行時,不等待異步調用的語句返回結果就執行後面的程序。web

同步調用

下面經過一個簡單示例來直觀的理解什麼是同步調用:spring

       定義Task類,建立三個處理函數分別模擬三個執行任務的操做,操做消耗時間隨機取(10秒內)併發

package com.dxz.demo1;

import java.util.Random;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * 定義3個任務
 */
@Component
public class Task1 {
    
    // 定義一個隨機對象.
    public static Random random = new Random();

    // 任務一;
    public void doTaskOne() throws Exception {
        System.out.println("開始作任務一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任務一,耗時:" + (end - start) + "毫秒");
    }

    // 任務二;
    public void doTaskTwo() throws Exception {
        System.out.println("開始作任務二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任務二,耗時:" + (end - start) + "毫秒");
    }

    // 任務3;
    public void doTaskThree() throws Exception {
        System.out.println("開始作任務三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任務三,耗時:" + (end - start) + "毫秒");
    }
    

}

編寫一個訪問方法:app

package com.dxz.demo1;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.dxz.HelloApplication;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = HelloApplication.class)
public class Task1Test {

    @Autowired
    private Task1 task1;
    
    //測試task1.
    @Test
    public void task1() throws Exception{
       task1.doTaskOne();
       task1.doTaskTwo();
       task1.doTaskThree();
    }
}

運行能夠看到相似以下輸出:dom

開始作任務一
2017-04-28 18:02:57.397  WARN 11016 --- [cTaskExecutor-1] o.s.a.r.l.SimpleMessageListenerContainer : Consumer raised exception, processing can restart if the connection factory supports it. Exception summary: org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect
2017-04-28 18:02:57.398  INFO 11016 --- [cTaskExecutor-1] o.s.a.r.l.SimpleMessageListenerContainer : Restarting Consumer: tags=[{}], channel=null, acknowledgeMode=AUTO local queue size=0
完成任務一,耗時:7740毫秒
開始作任務二
完成任務二,耗時:723毫秒
開始作任務三
2017-04-28 18:03:03.415  WARN 11016 --- [cTaskExecutor-2] o.s.a.r.l.SimpleMessageListenerContainer : Consumer raised exception, processing can restart if the connection factory supports it. Exception summary: org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect
2017-04-28 18:03:03.415  INFO 11016 --- [cTaskExecutor-2] o.s.a.r.l.SimpleMessageListenerContainer : Restarting Consumer: tags=[{}], channel=null, acknowledgeMode=AUTO local queue size=0
完成任務三,耗時:5047毫秒

異步調用

上述的同步調用雖然順利的執行完了三個任務,可是能夠看到執行時間比較長,若這三個任務自己之間不存在依賴關係,能夠併發執行的話,同步調用在執行效率方面就比較差,能夠考慮經過異步調用的方式來併發執行。異步

在Spring Boot中,咱們只須要經過使用@Async註解就能簡單的將原來的同步函數變爲異步函數,Task類改在爲以下模式:函數

package com.dxz.demo1;

import java.util.Random;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * 定義3個任務
 */
@Component
public class Task2 {
    
    // 定義一個隨機對象.
    public static Random random = new Random();

    // 任務一;
    @Async
    public void doTaskOne() throws Exception {
        System.out.println("開始作任務一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任務一,耗時:" + (end - start) + "毫秒");
    }

    // 任務二;
    @Async
    public void doTaskTwo() throws Exception {
        System.out.println("開始作任務二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任務二,耗時:" + (end - start) + "毫秒");
    }

    // 任務3;
    @Async
    public void doTaskThree() throws Exception {
        System.out.println("開始作任務三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任務三,耗時:" + (end - start) + "毫秒");
    }
    

}

  爲了讓@Async註解可以生效,還須要在Spring Boot的主程序中配置@EnableAsync,以下所示:單元測試

package com.dxz;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@SpringBootApplication
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}

編寫測試方法:測試

package com.dxz.demo1;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestMapping;

import com.dxz.HelloApplication;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = HelloApplication.class)
public class Task2Test {

    @Autowired
    private Task2 task2;
    
    //測試task1.
    @Test
    public void task1() throws Exception{
       task2.doTaskOne();
       task2.doTaskTwo();
       task2.doTaskThree();
    }
}

此時能夠反覆執行單元測試,您可能會遇到各類不一樣的結果,好比:

開始作任務一
開始作任務二
開始作任務三

修改下測試類:

package com.dxz.demo1;

import java.util.concurrent.TimeUnit;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestMapping;

import com.dxz.HelloApplication;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = HelloApplication.class)
public class Task2Test {

    @Autowired
    private Task2 task2;

    // 測試task1.
    @Test
    public void task1() throws Exception {
        task2.doTaskOne();
        task2.doTaskTwo();
        task2.doTaskThree();

        System.out.println("i'm here");
        TimeUnit.SECONDS.sleep(15);
        System.out.println("over");
    }

}

 jieguo:

i'm here開始作任務二開始作任務一開始作任務三完成任務三,耗時:1280毫秒2017-04-28 18:25:36.936 WARN 17848 --- [cTaskExecutor-1] o.s.a.r.l.SimpleMessageListenerContainer : Consumer raised exception, processing can restart if the connection factory supports it. Exception summary: org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect2017-04-28 18:25:36.938 INFO 17848 --- [cTaskExecutor-1] o.s.a.r.l.SimpleMessageListenerContainer : Restarting Consumer: tags=[{}], channel=null, acknowledgeMode=AUTO local queue size=0完成任務一,耗時:4951毫秒完成任務二,耗時:7451毫秒2017-04-28 18:25:42.971 WARN 17848 --- [cTaskExecutor-2] o.s.a.r.l.SimpleMessageListenerContainer : Consumer raised exception, processing can restart if the connection factory supports it. Exception summary: org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect2017-04-28 18:25:42.972 INFO 17848 --- [cTaskExecutor-2] o.s.a.r.l.SimpleMessageListenerContainer : Restarting Consumer: tags=[{}], channel=null, acknowledgeMode=AUTO local queue size=0over

相關文章
相關標籤/搜索