JAVA線程異步併發執行


先感謝微信一個老哥 薛華隆老哥的指導~~

多線程簡述

我的理解就是解決一個請求在執行業務時能夠併發執行,將本該一個人做的事情分別交給幾個人做,具體詳解請自行檢索

線程實操

線程池創建

注意:方式有多種,你也可以使用jdk1.8版本的自帶的線程池,我這裏還是使用Executor的子類ExecutorService

/**
 * 初始化SpringBean 工廠類
 */
@Component
@Configuration
public class BeanComponents {
	/**
	 * 線程池初始化對象
	 * 根據自己的實際業務進行擴展,參數數據自行配置
	 * 我這裏的環境就是,業務方法執行最高7條曲線,所以我設置爲10個線程初始化,足夠用
	 * @return
	 */
	@Bean
	public ExecutorService executorService(){
		return  new ThreadPoolExecutor(5,//池中保持線程數
			15,//最大線程數
			60,//當線程數大於核心時,終止前多餘空閒線程等待時間
			TimeUnit.SECONDS,//時間單位爲秒
			new LinkedBlockingDeque<>(10),//池內隊列最高10個線程
			Executors.defaultThreadFactory(),//執行程序創建新線程使用默認工廠
			new ThreadPoolExecutor.DiscardPolicy());//出現阻塞時使用處理程序
	}
}

創建線程返回基類

何爲基類?
這個就是說你要將每個線程執行完的返回結果放到線程池裏面去。
注意;我發現SpringBoot在加載普通類時並沒有提供IOC注入,通常我在注入某個API時總是NULL,
這裏我提供了一種方法:{首先將當前類靜態化屬性,使用初始化方法將依賴API注入當前類即可使用}

import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
import org.tfcloud.energy.dto.IndexValueDto;
import org.tfcloud.energy.dto.MachineAnalysisDto;
import org.tfcloud.energy.service.impl.MongoEverServiceImpl;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.concurrent.Callable;
//我這裏選擇實現Callable接口執行線程~
@Data
@SuppressWarnings("all")
@AllArgsConstructor
@Component
public class ThreadDtoUtils implements Callable<MachineAnalysisDto> {
	@Autowired
	private MongoEverServiceImpl mongoEverService;
	public static ThreadDtoUtils threadDtoUtils;

	/**
	 * 初始化當前類時即賦值-用於SpringBoot普通類注入對象爲空
	 */
	@PostConstruct
	public  void inits(){
		threadDtoUtils=this;
		threadDtoUtils.mongoEverService=this.mongoEverService;
	}
	/**
	 * 指標名稱
	 */
	private String paramName;
	@Transient
	private List<String> timeList;
	@Transient
	private String machineCode;
	@Transient
	private MongoTemplate mongoTemplateEnergy;
	@Transient
	private MongoTemplate mongoTemplateDye;

	/**
	 * 機臺存在多個指標名稱-集合{指標名稱|用量值}
	 */
	private List<IndexValueDto> indexValueDtoList;
	public ThreadDtoUtils(){
	}
	public ThreadDtoUtils(String paramName,List<String> timeList,String machineCode, MongoTemplate mongoTemplateEnergy,MongoTemplate mongoTemplateDye){
		this.paramName=paramName;
		this.timeList=timeList;
		this.machineCode=machineCode;
		this.mongoTemplateEnergy=mongoTemplateEnergy;
		this.mongoTemplateDye=mongoTemplateDye;
	}
	//核心方法,異步執行該方法進行業務處理,
	@Override
	public MachineAnalysisDto call() throws Exception {
		System.out.println("*********線程執行"+Thread.currentThread().getName()+"執行");
		MachineAnalysisDto machineAnalysisDto = threadDtoUtils.mongoEverService.
			getMachineAnalysisDto(paramName, timeList, machineCode, mongoTemplateEnergy, mongoTemplateDye);
		return machineAnalysisDto;
	}
}

異步線程執行方法接收

我這裏的業務是根據paramTypes.size()這個數字進行線程執行次數,如果是8次,那就是循環8次,
那麼這8次執行就會分8個線程,每條線程互不相干,每執行完一個線程,就會添加至線程存儲Future中

//線程結果存儲
		List<Future<?>> futures = new ArrayList<>(paramTypes.size());
		for (int i = 0; i < paramTypes.size(); i++) {
			ThreadDtoUtils threadDtoUtils = new ThreadDtoUtils(paramTypes.get(i), timeDiffs, machineCode, mongoTemplateEnergy, mongoTemplateDye);
			//異步非阻塞執行異常處理
			Future<MachineAnalysisDto> future = executorService.submit(threadDtoUtils);
			futures.add(future);
		}
		//這裏我就是將線程池中數據轉換成我想要的數據,業務自行擴展即可
		futures.stream().forEach(item -> {
			try {
				//轉換類就是之前繼承Callable的那個類
				MachineAnalysisDto machineAnalysisDto = (MachineAnalysisDto) item.get();
				//這裏我是根據日期排了一個序,這個不用管~~
				machineAnalysisDto.getIndexValueDtoList().sort((s1,s2) -> s1.getCurrDate().compareTo(s2.getCurrDate()));
				//添加至我返回給前端展示的集合當中
				machineAnalysisDtos.add(machineAnalysisDto);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		});

總結

至此我還是發現我的不足之處,平時寫業務沒問題,就是在處理一些特殊業務時平時的技術就難以攻克
平時還是要多注重一些核心技術點,例如~多線程,高併發,等都需要自己動手實踐,舉一反三用於項目中,提高執行效率。

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