鄭州網(wǎng)站推廣排名公司浙江關(guān)鍵詞優(yōu)化
在開發(fā)過程中,我們難免會(huì)因?yàn)樾阅?、?shí)時(shí)響應(yīng)等,需要異步處理的一些事務(wù),并且在子線程中有時(shí)我們還需要獲取主線程相關(guān)的參數(shù)。下面有若干方案可以實(shí)現(xiàn)上述場(chǎng)景,但會(huì)出現(xiàn)一定的問題。
場(chǎng)景1-基礎(chǔ)場(chǎng)景
在主線程中開啟子線程,在子線程中獲取主線程的參數(shù)。
重點(diǎn):子線程中邏輯處理時(shí)間較短,在主線程結(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ù)。
場(chǎng)景2-場(chǎng)景1的變種
在主線程中開啟子線程,在子線程中獲取主線程的參數(shù)。
重點(diǎn):子線程在執(zhí)行一段時(shí)間后再獲取主線程的參數(shù),這個(gè)時(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了一秒,這個(gè)時(shí)候主線程已經(jīng)執(zhí)行完成,子線程如果想繼續(xù)獲取主線程的參數(shù),就會(huì)拿不到值。
場(chǎng)景3-場(chǎng)景1的完善
在主線程中開啟子線程,在子線程中獲取主線程的參數(shù)。
重點(diǎn):子線程在執(zhí)行一段時(shí)間后再獲取主線程的參數(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ǎng),但仍可以獲取主線程的參數(shù),主線程在子線程執(zhí)行完成后再結(jié)束。
主要技術(shù):通過future.get();來使主線程阻塞。
缺點(diǎn):主線程等待時(shí)間較長(zhǎng),消息無法實(shí)時(shí)返回,需要等待子線程執(zhí)行完成后再返回。
場(chǎng)景4-場(chǎng)景1、2、3的優(yōu)化
在主線程中開啟子線程,在子線程中獲取主線程的參數(shù)。
重點(diǎn):子線程在執(zhí)行一段時(shí)間后再獲取主線程的參數(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ǎng),但仍可以獲取主線程的參數(shù),主線程無需等待子線程執(zhí)行完成,可立即返回。
核心技術(shù)點(diǎn):
- 開啟異步
AsyncContext asyncContext = request.startAsync();
- 子線程執(zhí)行完后調(diào)用:
asyncContext.complete();
具體原理:可閱讀源碼。
彩蛋
場(chǎng)景4在部分框架下失效,如項(xiàng)目中引用Spring- Security框架等,會(huì)導(dǎo)致主線程開啟子線程后阻塞,具體原因待分析。其他場(chǎng)景下可正常使用。