一、前言
在程序中,代碼是順序執行的,但是這種順序執行的方式在某些情況下效率很低,就拿年夜飯來舉例子吧,過年要吃很多好吃的,比如說你家有兩個炒鍋,一個高壓鍋(但是用的時候都是隻用一個),你們要吃燉排骨,然後炒十個菜,只有媽媽一個人在做,媽媽要先燉排骨,然後炒第一個菜,這樣順序執行下來。但是全家人得等着啊,太慢了是不是?這時候媽媽如果將三個鍋同時用起來,就可以炒兩個菜和燉一個排骨。這裏面的鍋就相當於線程池,媽媽是就是cpu了(核心啊)。這個時候媽媽發揮生平所學,用着兩個鍋炒菜,用着一個鍋燉排骨,每個鍋都是同時進行的,但是每個鍋的菜卻不一定一起熟,同時媽媽通過品嚐的方法來判斷飯菜是否已經熟了,這個就是線程的返回結果。
二、環境和思路
spring boot項目
任務通過設置任務key來區別不同的任務,如果任務執行成功,則返回該任務key,否則返回error,代表任務失敗,同時附帶失敗原因碼和說明。
三、實戰
1、線程池配置類ThreadPoolConfig
package com.zlc.multithreading.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 11:49
**/
@Configuration
public class ThreadPoolConfig {
/**
* 核心線程池的大小是:75
* 線程池的最大數量:125
* 空閒線程的存活時間:180000毫秒
* 空閒線程的存活時間的單位:ms
* 工作隊列:基於鏈表結構的阻塞隊列
* 飽和策略:沒有聲明默認採用CallerRunsPolicy (由調用線程處理該任務)
*/
@Bean
public ExecutorService getThreadPool(){
return new ThreadPoolExecutor(75,
125,
180000,
TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(450),
new ThreadPoolExecutor.CallerRunsPolicy());
}
}
2、封裝一個任務返回對象TaskResponseModel
package com.zlc.multithreading.model;
import lombok.Data;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 11:44
**/
@Data
public class TaskResponseModel {
private String key;
private Integer resultCode;
private String resultMessage;
}
3、任務請求參數對象RequestObjectModel
package com.zlc.multithreading.model;
import lombok.Data;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 10:44
**/
@Data
public class RequestObjectModel {
private String key;
private Object params;
private Object request;
}
4、多線程併發任務TaskExecutor
package com.zlc.multithreading.task;
import com.zlc.multithreading.constance.TaskConstance;
import com.zlc.multithreading.model.TaskResponseModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 11:47
**/
@Component
public class TaskExecutor {
private static final Logger logger = LoggerFactory.getLogger(TaskExecutor.class);
/**
* 線程池
*/
private final ExecutorService executorService;
public TaskExecutor(ExecutorService executorService) {
this.executorService = executorService;
}
/**
* 執行任務,任務列表需要從外邊構造好,然後傳進來
**/
public List<TaskResponseModel> execute(List<Callable<TaskResponseModel>> commands) {
//創建異步執行對象
CompletionService<TaskResponseModel> completionService = new ExecutorCompletionService<>(executorService);
for (Callable<TaskResponseModel> command : commands) {
completionService.submit(command);
}
//響應參數
int taskCount = commands.size();
List<TaskResponseModel> params = new ArrayList<>(taskCount);
try {
for (int i = 0; i < taskCount; i++) {
Future future = completionService.take();
params.add((TaskResponseModel) future.get());
}
} catch (InterruptedException | ExecutionException e) {
TaskResponseModel taskResponseModel = new TaskResponseModel();
taskResponseModel.setKey(TaskConstance.TASK_ERROR);
params.add(taskResponseModel);
logger.error(e.getMessage());
}
return params;
}
}
5、創建5個測試業務service,每個業務的休眠時間不同來代表不同業務的處理時間
1)TestServiceOne
package com.zlc.multithreading.service;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 14:30
**/
public interface TestServiceOne {
TaskResponseModel testOne(String key, RequestObjectModel requestObjectModel);
}
package com.zlc.multithreading.service;
import com.zlc.multithreading.constance.TaskConstance;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
import org.springframework.stereotype.Service;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 14:41
**/
@Service
public class TestServiceOneImpl implements TestServiceOne{
@Override
public TaskResponseModel testOne(String key, RequestObjectModel requestObjectModel) {
TaskResponseModel taskResponseModel = new TaskResponseModel();
// 測試一的處理流程
long startTime = System.currentTimeMillis();
try {
// 讓這個任務休眠100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
// 根據具體業務校驗或者是處理或者該任務拋出異常的話,返回的key爲error
taskResponseModel.setKey(TaskConstance.TASK_ERROR);
// 可以將resultCode和resultMessage統一定義一下
// 比如說將測試一的返回碼設置爲1開頭
// 測試一任務異常 設置爲子code爲10000,對應的message爲 任務一系統異常
// 將 沒通過某種規則 設置爲子code爲 11000, 對應的message爲 沒通過任務一校驗規則
// 以此類推
taskResponseModel.setResultCode(TaskConstance.TASK_ONE_EXPECTION_CODE);
taskResponseModel.setResultMessage("測試一任務異常啦,異常信息爲:" + e.getMessage());
}
long endTime = System.currentTimeMillis();
// 如果沒有異常和出發不通過規則,則返回你想返回的數據,demo以返回處理時間爲例
taskResponseModel.setKey(TaskConstance.TASK_ONE_KEY);
taskResponseModel.setResultCode(TaskConstance.TASK_SUCCESS_CODE);
taskResponseModel.setResultMessage("測試一任務處理時間爲:" + (endTime-startTime) + "毫秒");
return taskResponseModel;
}
}
2)TestServiceTwo
package com.zlc.multithreading.service;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 14:31
**/
public interface TestServiceTwo {
TaskResponseModel testTwo(String key, RequestObjectModel requestObjectModel);
}
package com.zlc.multithreading.service;
import com.zlc.multithreading.constance.TaskConstance;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
import org.springframework.stereotype.Service;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 15:09
**/
@Service
public class TestServiceTwoImpl implements TestServiceTwo {
@Override
public TaskResponseModel testTwo(String key, RequestObjectModel requestObjectModel) {
TaskResponseModel taskResponseModel = new TaskResponseModel();
// 測試二的處理流程
long startTime = System.currentTimeMillis();
try {
// 讓這個任務休眠600毫秒
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
// 根據具體業務校驗或者是處理或者該任務拋出異常的話,返回的key爲error
taskResponseModel.setKey(TaskConstance.TASK_ERROR);
// 可以將resultCode和resultMessage統一定義一下
// 比如說將測試一的返回碼設置爲1開頭
// 測試一任務異常 設置爲子code爲10000,對應的message爲 任務一系統異常
// 將 沒通過某種規則 設置爲子code爲 11000, 對應的message爲 沒通過任務一校驗規則
// 以此類推
taskResponseModel.setResultCode(TaskConstance.TASK_TWO_EXPECTION_CODE);
taskResponseModel.setResultMessage("測試二任務異常啦,異常信息爲:" + e.getMessage());
}
long endTime = System.currentTimeMillis();
// 如果沒有異常和出發不通過規則,則返回你想返回的數據,demo以返回處理時間爲例
taskResponseModel.setKey(TaskConstance.TASK_TWO_KEY);
taskResponseModel.setResultCode(TaskConstance.TASK_SUCCESS_CODE);
taskResponseModel.setResultMessage("測試二任務處理時間爲:" + (endTime-startTime) + "毫秒");
return taskResponseModel;
}
}
3)TestServiceThree
package com.zlc.multithreading.service;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 14:31
**/
public interface TestServiceThree {
TaskResponseModel testThree(String key, RequestObjectModel requestObjectModel);
}
package com.zlc.multithreading.service;
import com.zlc.multithreading.constance.TaskConstance;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
import org.springframework.stereotype.Service;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 15:11
**/
@Service
public class TestServiceThreeImpl implements TestServiceThree {
@Override
public TaskResponseModel testThree(String key, RequestObjectModel requestObjectModel) {
TaskResponseModel taskResponseModel = new TaskResponseModel();
// 測試三的處理流程
long startTime = System.currentTimeMillis();
try {
// 讓這個任務休眠300毫秒
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
// 根據具體業務校驗或者是處理或者該任務拋出異常的話,返回的key爲error
taskResponseModel.setKey(TaskConstance.TASK_ERROR);
// 可以將resultCode和resultMessage統一定義一下
// 比如說將測試一的返回碼設置爲1開頭
// 測試一任務異常 設置爲子code爲10000,對應的message爲 任務一系統異常
// 將 沒通過某種規則 設置爲子code爲 11000, 對應的message爲 沒通過任務一校驗規則
// 以此類推
taskResponseModel.setResultCode(TaskConstance.TASK_THREE_EXPECTION_CODE);
taskResponseModel.setResultMessage("測試三任務異常啦,異常信息爲:" + e.getMessage());
}
long endTime = System.currentTimeMillis();
// 如果沒有異常和出發不通過規則,則返回你想返回的數據,demo以返回處理時間爲例
taskResponseModel.setKey(TaskConstance.TASK_THREE_KEY);
taskResponseModel.setResultCode(TaskConstance.TASK_SUCCESS_CODE);
taskResponseModel.setResultMessage("測試三任務處理時間爲:" + (endTime-startTime) + "毫秒");
return taskResponseModel;
}
}
4)TestServiceFour
package com.zlc.multithreading.service;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 14:32
**/
public interface TestServiceFour {
TaskResponseModel testFour(String key, RequestObjectModel requestObjectModel);
}
package com.zlc.multithreading.service;
import com.zlc.multithreading.constance.TaskConstance;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
import org.springframework.stereotype.Service;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 15:13
**/
@Service
public class TestServiceFourImpl implements TestServiceFour {
@Override
public TaskResponseModel testFour(String key, RequestObjectModel requestObjectModel) {
TaskResponseModel taskResponseModel = new TaskResponseModel();
// 測試四的處理流程
long startTime = System.currentTimeMillis();
try {
// 讓這個任務休眠800毫秒
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
// 根據具體業務校驗或者是處理或者該任務拋出異常的話,返回的key爲error
taskResponseModel.setKey(TaskConstance.TASK_ERROR);
// 可以將resultCode和resultMessage統一定義一下
// 比如說將測試一的返回碼設置爲1開頭
// 測試一任務異常 設置爲子code爲10000,對應的message爲 任務一系統異常
// 將 沒通過某種規則 設置爲子code爲 11000, 對應的message爲 沒通過任務一校驗規則
// 以此類推
taskResponseModel.setResultCode(TaskConstance.TASK_FOUR_EXPECTION_CODE);
taskResponseModel.setResultMessage("測試四任務異常啦,異常信息爲:" + e.getMessage());
}
long endTime = System.currentTimeMillis();
// 如果沒有異常和出發不通過規則,則返回你想返回的數據,demo以返回處理時間爲例
taskResponseModel.setKey(TaskConstance.TASK_FOUR_KEY);
taskResponseModel.setResultCode(TaskConstance.TASK_SUCCESS_CODE);
taskResponseModel.setResultMessage("測試二任務處理時間爲:" + (endTime-startTime) + "毫秒");
return taskResponseModel;
}
}
6、創建四個測試任務實現Callable,需要重寫call方法,是有返回值的,設置任務的key,請求參數,以及具體業務處理的service;
1)任務一
package com.zlc.multithreading.callback;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
import com.zlc.multithreading.service.TestServiceOne;
import java.util.concurrent.Callable;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 14:38
**/
public class TestOneCallBack implements Callable<TaskResponseModel> {
private String key;
private RequestObjectModel requestObjectModel;
private final TestServiceOne testServiceOne;
public TestOneCallBack(String key, RequestObjectModel requestObjectModel, TestServiceOne testServiceOne) {
this.setKey(key);
this.setRequestObjectModel(requestObjectModel);
this.testServiceOne = testServiceOne;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public RequestObjectModel getRequestObjectModel() {
return requestObjectModel;
}
public void setRequestObjectModel(RequestObjectModel requestObjectModel) {
this.requestObjectModel = requestObjectModel;
}
@Override
public TaskResponseModel call() {
return testServiceOne.testOne(key,requestObjectModel);
}
}
2)任務二
package com.zlc.multithreading.callback;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
import com.zlc.multithreading.service.TestServiceTwo;
import java.util.concurrent.Callable;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 14:38
**/
public class TestTwoCallBack implements Callable<TaskResponseModel> {
private String key;
private RequestObjectModel requestObjectModel;
private final TestServiceTwo testServiceTwo;
public TestTwoCallBack(String key, RequestObjectModel requestObjectModel, TestServiceTwo testServiceTwo) {
this.setKey(key);
this.setRequestObjectModel(requestObjectModel);
this.testServiceTwo = testServiceTwo;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public RequestObjectModel getRequestObjectModel() {
return requestObjectModel;
}
public void setRequestObjectModel(RequestObjectModel requestObjectModel) {
this.requestObjectModel = requestObjectModel;
}
@Override
public TaskResponseModel call() {
return testServiceTwo.testTwo(key,requestObjectModel);
}
}
3)任務三
package com.zlc.multithreading.callback;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
import com.zlc.multithreading.service.TestServiceThree;
import java.util.concurrent.Callable;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 14:38
**/
public class TestThreeCallBack implements Callable<TaskResponseModel> {
private String key;
private RequestObjectModel requestObjectModel;
private final TestServiceThree testServiceThree;
public TestThreeCallBack(String key, RequestObjectModel requestObjectModel, TestServiceThree testServiceThree) {
this.setKey(key);
this.setRequestObjectModel(requestObjectModel);
this.testServiceThree = testServiceThree;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public RequestObjectModel getRequestObjectModel() {
return requestObjectModel;
}
public void setRequestObjectModel(RequestObjectModel requestObjectModel) {
this.requestObjectModel = requestObjectModel;
}
@Override
public TaskResponseModel call() {
return testServiceThree.testThree(key,requestObjectModel);
}
}
4)任務四
package com.zlc.multithreading.callback;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
import com.zlc.multithreading.service.TestServiceFour;
import java.util.concurrent.Callable;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 14:38
**/
public class TestFourCallBack implements Callable<TaskResponseModel> {
private String key;
private RequestObjectModel requestObjectModel;
private final TestServiceFour testServiceFour;
public TestFourCallBack(String key, RequestObjectModel requestObjectModel, TestServiceFour testServiceFour) {
this.setKey(key);
this.setRequestObjectModel(requestObjectModel);
this.testServiceFour = testServiceFour;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public RequestObjectModel getRequestObjectModel() {
return requestObjectModel;
}
public void setRequestObjectModel(RequestObjectModel requestObjectModel) {
this.requestObjectModel = requestObjectModel;
}
@Override
public TaskResponseModel call() {
return testServiceFour.testFour(key,requestObjectModel);
}
}
7、創建一個Controller調用一下TestController
package com.zlc.multithreading.controller;
import com.zlc.multithreading.callback.TestFourCallBack;
import com.zlc.multithreading.callback.TestOneCallBack;
import com.zlc.multithreading.callback.TestThreeCallBack;
import com.zlc.multithreading.callback.TestTwoCallBack;
import com.zlc.multithreading.model.RequestObjectModel;
import com.zlc.multithreading.model.TaskResponseModel;
import com.zlc.multithreading.service.TestServiceFour;
import com.zlc.multithreading.service.TestServiceOne;
import com.zlc.multithreading.service.TestServiceThree;
import com.zlc.multithreading.service.TestServiceTwo;
import com.zlc.multithreading.task.TaskExecutor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
/**
* @author : 追到烏雲的盡頭找太陽-(Jacob)
* @date : 2020/1/21 14:59
**/
@RestController
public class TestController {
private final TestServiceOne testServiceOne;
private final TestServiceTwo testServiceTwo;
private final TestServiceThree testServiceThree;
private final TestServiceFour testServiceFour;
private final TaskExecutor taskExecutor;
public TestController(TestServiceOne testServiceOne, TestServiceTwo testServiceTwo,
TestServiceThree testServiceThree, TestServiceFour testServiceFour,
TaskExecutor taskExecutor) {
this.testServiceOne = testServiceOne;
this.testServiceTwo = testServiceTwo;
this.testServiceThree = testServiceThree;
this.testServiceFour = testServiceFour;
this.taskExecutor = taskExecutor;
}
@GetMapping(value = "testTask")
public void testTask(){
// 將併發任務填入,準備執行
List<Callable<TaskResponseModel>> baseTaskCallbackList = new ArrayList<>(4);
// 添加測試一任務
baseTaskCallbackList.add(this.testOne());
// 添加測試二任務
baseTaskCallbackList.add(this.testTwo());
// 添加測試三任務
baseTaskCallbackList.add(this.testThree());
// 添加測試四任務
baseTaskCallbackList.add(this.testFour());
// 執行任務,任務執行根據每個任務實際執行結束的時間返回,也就是說沒有固定先後順序
List<TaskResponseModel> taskResponseModels = taskExecutor.execute(baseTaskCallbackList);
// 查看每個任務的執行結果
for (TaskResponseModel responseModel: taskResponseModels){
// 看打印結果就可以發現,不管添加任務的先後順序,哪個任務先執行完,就先返回
// 如果任務拋出異常或者沒有通過任務規則,則通過設置key來表示任務失敗
// 任務失敗原因通過resultCode和resultMessage來說明
System.out.println(responseModel);
}
}
private TestOneCallBack testOne(){
String key = "TestOne";
RequestObjectModel requestObjectModel = new RequestObjectModel();
return new TestOneCallBack(key, requestObjectModel, this.testServiceOne);
}
private TestTwoCallBack testTwo(){
String key = "TestTwo";
RequestObjectModel requestObjectModel = new RequestObjectModel();
return new TestTwoCallBack(key, requestObjectModel, this.testServiceTwo);
}
private TestThreeCallBack testThree(){
String key = "TestThree";
RequestObjectModel requestObjectModel = new RequestObjectModel();
return new TestThreeCallBack(key, requestObjectModel, this.testServiceThree);
}
private TestFourCallBack testFour(){
String key = "TestFour";
RequestObjectModel requestObjectModel = new RequestObjectModel();
return new TestFourCallBack(key, requestObjectModel, this.testServiceFour);
}
}
四、測試
啓動項目以後,在瀏覽器或者用POSTMAN調用一下Controller接口就可以得到結果了,測試結果如下圖: