多線程併發實戰

一、前言

在程序中,代碼是順序執行的,但是這種順序執行的方式在某些情況下效率很低,就拿年夜飯來舉例子吧,過年要吃很多好吃的,比如說你家有兩個炒鍋,一個高壓鍋(但是用的時候都是隻用一個),你們要吃燉排骨,然後炒十個菜,只有媽媽一個人在做,媽媽要先燉排骨,然後炒第一個菜,這樣順序執行下來。但是全家人得等着啊,太慢了是不是?這時候媽媽如果將三個鍋同時用起來,就可以炒兩個菜和燉一個排骨。這裏面的鍋就相當於線程池,媽媽是就是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接口就可以得到結果了,測試結果如下圖:
在這裏插入圖片描述

五、都到這裏了,掃碼關注一下微信公衆號吧,您的支持是對我最大的鼓勵!

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章