非同步呼叫和同步呼叫 及 spring的@Async註解

蔡瘦瘦子發表於2020-12-29

1. 何為非同步呼叫?

在解釋非同步呼叫之前,我們先來看同步呼叫的定義;
同步呼叫就是整個處理過程順序執行,當各個過程都執行完畢,並返回結果。
非同步呼叫則是隻是傳送了呼叫的指令,呼叫者無需等待被呼叫的方法完全執行完畢,而是繼續執行下面的流程。
例如, 在某個呼叫中,需要順序呼叫 A, B, C三個過程方法;如他們都是同步呼叫,則需要將他們都順序執行完畢之後,方算作過程執行完畢; 如B為一個非同步的呼叫方法,則在執行完A之後,呼叫B,並不等待B完成,而是執行開始呼叫C,待C執行完畢之後,就意味著這個過程執行完畢了。

2. 常規的非同步呼叫處理方式

在Java中,一般在處理類似的場景之時,都是基於建立獨立的執行緒去完成相應的非同步呼叫邏輯,通過主執行緒和不同的執行緒之間的執行流程,從而在啟動獨立的執行緒之後,主執行緒繼續執行而不會產生停滯等待的情況。

3. @Async介紹

在Spring中,基於@Async標註的方法,稱之為非同步方法;這些方法將在執行的時候,將會在獨立的執行緒中被執行,呼叫者無需等待它的完成,即可繼續其他的操作。

分為不帶引數的非同步呼叫;帶引數的非同步呼叫;呼叫返回Future的非同步執行緒

4. @Async呼叫中的事務處理機制

在@Async標註的方法,同時也適用了@Transactional進行了標註;在其呼叫資料庫操作之時,將無法產生事務管理的控制,原因就在於其是基於非同步處理的操作。 那該如何給這些操作新增事務管理呢?可以將需要事務管理操作的方法放置到非同步方法內部,在內部被呼叫的方法上新增@Transactional註解。
例如: 方法A,使用了@Async/@Transactional來標註,但是無法產生事務控制的目的。 方法B,使用了@Async來標註, B中呼叫了C、D,C/D分別使用@Transactional做了標註,則可實現事務控制的目的。

5. 配合使用@EnableAsync

@EnableAsync
在啟動類或者Control類加上 @EnableAsync 註解

@EnableAsync註解的意思是可以非同步執行,就是開啟多執行緒的意思。可以標註在方法、類上。@Async所修飾的函式不要定義為static型別,這樣非同步呼叫不會生效。
如下:

@SpringBootApplication
@EnableAsync
public class Application
{
    public static void main( String[] args )
    {
        SpringApplication.run(Application.class, args);
    }
}

或者:

@EnableAsync
@RestController
public class HelloController {
    @Autowired
    TestAsyncService testAsyncService;
}

6. 舉例:

兩張表:user_info和order_table 插入user_info資料時候用同步,插入order_table用非同步。

在controller類中建立一個方法 同時儲存user_info和order_table表。儲存order_table用非同步(對應service方法中用@Async標註)

(1)domain資料夾中建立Entity類

package com.cfj.ceshi.async.domain;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="order_table")
public class OrderTable implements Serializable {

    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    
    @Column(name = "order_name")
    private String orderName;
    
    @Column(name = "user_id")
    private Integer userId; 
    
    @Column(name = "create_date")
    private Date createDate;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getOrderName() {
        return orderName;
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }
}
package com.cfj.ceshi.async.domain;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="user_info")
public class UserInfo implements Serializable {

    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String userName;
    private String age;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "UserInfo [id=" + id + ", userName=" + userName + ", age=" + age + "]";
    }    
}

(2)建立repository層運算元據庫。如果是普通儲存方法,只需要介面繼承JpaRepository,不需要寫具體方法

package com.cfj.ceshi.async.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import com.cfj.ceshi.async.domain.OrderTable;

public interface OrderRepository extends JpaRepository<OrderTable, Integer> {

}
package com.cfj.ceshi.async.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import com.cfj.ceshi.async.domain.UserInfo;

public interface UserRepository extends JpaRepository<UserInfo, Integer> {
          
}

(3)service層 其中order的實現層儲存方法加上@Async

package com.cfj.ceshi.async.service;

public interface OrderService {  
    public void saveOrder(Integer UserId,String name);
}
package com.cfj.ceshi.async.service;

import java.util.List;
import com.cfj.ceshi.async.domain.UserInfo;

public interface UserService {  
    public Integer save(UserInfo user);
}
package com.cfj.ceshi.async.service.impl;

import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.cfj.ceshi.async.domain.OrderTable;
import com.cfj.ceshi.async.repository.OrderRepository;
import com.cfj.ceshi.async.service.OrderService;

@Service
public class OrderServiceImpl implements OrderService {  
    @Autowired
    OrderRepository orderRepository;
      
    /**
     * 非同步儲存
     */
    @Async
    @Override
    public void saveOrder(Integer UserId,String name) {
        System.out.println("UserId:"+UserId);
        System.out.println("=====" + Thread.currentThread().getName() + "=========");
        OrderTable orderTable = new OrderTable();
        
        orderTable.setOrderName(name+"訂單");
        orderTable.setUserId(UserId);
        orderTable.setCreateDate(new Date());
        orderRepository.save(orderTable);       
    }
}
package com.cfj.ceshi.async.service.impl;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.cfj.ceshi.async.domain.UserInfo;
import com.cfj.ceshi.async.repository.UserRepository;
import com.cfj.ceshi.async.service.UserService;

@Service
@Transactional
public class UserServiceImpl implements UserService{ 
    @Autowired
    private UserRepository userRepository;
    
    @Override
    public Integer save(UserInfo user) {
        System.out.println("=====" + Thread.currentThread().getName() + "=========");
        return userRepository.save(user).getId();
    }    
}

(4) control層,control類中新增@EnableAsync註解

package com.cfj.ceshi.async.web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.cfj.ceshi.async.domain.UserInfo;
import com.cfj.ceshi.async.service.OrderService;
import com.cfj.ceshi.async.service.UserService;

@EnableAsync
@RestController
@RequestMapping("/tesasyc")
public class AsycWeb {    
    @Autowired
    UserService userService;
    @Autowired
    OrderService orderService;
    
    /**
     *    請使用 postman測試    方式選擇post  http://localhost:8081/tesasyc/save-one
     *  body 中選擇form-data 或者x-wwww-form-urlencoded  輸入對應鍵值對
     * @param name
     * @param age
     * @return
     */
    @PostMapping(value = "/save-one") //相當於@RequestMapping(value = "/save-one", method = RequestMethod.POST)
    public String postOne(String name,String age) {
        UserInfo user = new UserInfo();
        user.setUserName(name);
        user.setAge(age);
        Integer id = userService.save(user);
        orderService.saveOrder(id,name);
        return id.toString();        
    }
}

控制檯輸出如下:
在這裡插入圖片描述

相關文章