鄭州網(wǎng)站推廣排名公司浙江關(guān)鍵詞優(yōu)化
在開發(fā)過程中,我們難免會因為性能、實時響應(yīng)等,需要異步處理的一些事務(wù),并且在子線程中有時我們還需要獲取主線程相關(guān)的參數(shù)。下面有若干方案可以實現(xiàn)上述場景,但會出現(xiàn)一定的問題。
場景1-基礎(chǔ)場景
在主線程中開啟子線程,在子線程中獲取主線程的參數(shù)。
重點(diǎn):子線程中邏輯處理時間較短,在主線程結(jié)束前獲取主線程的參數(shù)。
package com.lihao.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** @author lihao*/
@RestController
@RequestMapping("/test1")
public class Test1 {/*** 自定義線程池*/private ExecutorService executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors(),5,TimeUnit.MINUTES,new LinkedBlockingQueue<>(100),Thread::new,new ThreadPoolExecutor.AbortPolicy());@GetMapping("/asyncTest")public String asyncTest(HttpServletRequest request) {request.setAttribute("key1","value1");// 異步處理任務(wù)executor.submit(() -> doExe(request));return "OK";}public void doExe(HttpServletRequest request){System.out.println("值:" + request.getAttribute("key1"));}
}
執(zhí)行結(jié)果:
值:value1
我們可以正常拿到主線程的參數(shù)。
場景2-場景1的變種
在主線程中開啟子線程,在子線程中獲取主線程的參數(shù)。
重點(diǎn):子線程在執(zhí)行一段時間后再獲取主線程的參數(shù),這個時候主線程已執(zhí)行完成了。
@GetMapping("/asyncTest")public String asyncTest(HttpServletRequest request) {request.setAttribute("key1","value1");// 異步處理任務(wù)executor.submit(() -> doExe(request,1000L));return "OK";}public void doExe(HttpServletRequest request,long sleepTime){try {Thread.sleep(sleepTime);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("值:" + request.getAttribute("key1"));}
執(zhí)行結(jié)果:
值:null
由于子線程sleep了一秒,這個時候主線程已經(jīng)執(zhí)行完成,子線程如果想繼續(xù)獲取主線程的參數(shù),就會拿不到值。
場景3-場景1的完善
在主線程中開啟子線程,在子線程中獲取主線程的參數(shù)。
重點(diǎn):子線程在執(zhí)行一段時間后再獲取主線程的參數(shù),主線程需要等待子線程執(zhí)行完成后,再結(jié)束。
@GetMapping("/asyncTest")public String asyncTest(HttpServletRequest request) {request.setAttribute("key1","value1");// 異步處理任務(wù)Future<?> future = executor.submit(() -> doExe(request, 10000L));try {future.get();} catch (InterruptedException | ExecutionException e) {throw new RuntimeException(e);}return "OK";}public void doExe(HttpServletRequest request,long sleepTime){try {Thread.sleep(sleepTime);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("值:" + request.getAttribute("key1"));}
雖然子線程執(zhí)行時間較長,但仍可以獲取主線程的參數(shù),主線程在子線程執(zhí)行完成后再結(jié)束。
主要技術(shù):通過future.get();來使主線程阻塞。
缺點(diǎn):主線程等待時間較長,消息無法實時返回,需要等待子線程執(zhí)行完成后再返回。
場景4-場景1、2、3的優(yōu)化
在主線程中開啟子線程,在子線程中獲取主線程的參數(shù)。
重點(diǎn):子線程在執(zhí)行一段時間后再獲取主線程的參數(shù),主線程無需要等待子線程執(zhí)行完成,可立即結(jié)束。
@GetMapping("/asyncTest")public String asyncTest(HttpServletRequest request) {request.setAttribute("key1","value1");// 開啟異步AsyncContext asyncContext = request.startAsync();executor.submit(() -> doExe(asyncContext,request, 10000L));return "OK";}public void doExe(AsyncContext asyncContext,HttpServletRequest request,long sleepTime){try {Thread.sleep(sleepTime);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("值:" + request.getAttribute("key1"));asyncContext.complete();}
雖然子線程執(zhí)行時間較長,但仍可以獲取主線程的參數(shù),主線程無需等待子線程執(zhí)行完成,可立即返回。
核心技術(shù)點(diǎn):
- 開啟異步
AsyncContext asyncContext = request.startAsync();
- 子線程執(zhí)行完后調(diào)用:
asyncContext.complete();
具體原理:可閱讀源碼。
彩蛋
場景4在部分框架下失效,如項目中引用Spring- Security框架等,會導(dǎo)致主線程開啟子線程后阻塞,具體原因待分析。其他場景下可正常使用。