多執行緒系列之 java多執行緒的個人理解(二)
前言:上一篇多執行緒系列之 java多執行緒的個人理解(一) 講到了執行緒、程序、多執行緒的基本概念,以及多執行緒在java中的基本實現方式,本篇主要接著上一篇繼續講述多執行緒在實際專案中的應用以及遇到的諸多問題和解決方案
文章結構:
- 多執行緒在實際專案中應用
- 多執行緒的優缺點
1.多執行緒在實際專案中應用
專案分享(一)
背景:重慶移動代維管理系統專案,主要負責對重慶移動各代維公司,分公司,代維人員,以及各類代維業務和資產的統籌管理;其中的裝維管理模組,是在代維繫統中佔有一席之地,主要保障 移動寬頻裝機的線上流程以及業務順利執行;在裝維管理中,裝機寬頻業務在代維繫統被抽象成了的工單的形式,每張工單又對應了唯一的流程實體。在整個流轉過程中,有很多關鍵環節,其中就有一個環節叫 竣工校驗歸檔。他要完成的工作是當裝機工單流程從完成安裝到歸檔前,應該通過調介面的方式校驗工單對應的寬頻賬號是否有上網記錄。
遇到的問題 :前期的做法是用單執行緒非同步的方式呼叫目標系統的介面,返回報文來完成業務。按照這樣的設計思路系統平穩的運行了一段時間;突然有一天開始,代維繫統的運維人員頻繁反映,裝機工單有很多單子堵塞在竣工校驗歸檔環節。通過日誌分析並定位是單執行緒導致的執行效率過慢,所以考慮到使用多執行緒,分批次完成工單的校驗任務。
具體解決思路:在業務背景下,由於每張工單在進入校驗佇列後在後臺數據庫中都儲存了一個執行時間,所以,通過與當前時間比較是否應該去調介面校驗,前期由於是單執行緒,考慮的問題很少。後來利用多執行緒,將多張進入佇列的工單分批次處理(即將執行時間的Long型資料對N取餘,假使有N+1個執行緒在併發執行),這樣每個執行緒所分配到的工單都是不同的,不會存在併發修改等多執行緒安全問題,當然這裡還是以繼承Thread類為例:
執行緒類:
public class Eoms2ProvThread4OldRadius extends Thread{
private static final String UPDATE_SQL = " UPDATE BS_T_WF4_FTTH I SET I.FLAG = ? , I.CALLS = ?, I.ERRORINFO = ?,RECORDTIME = ? WHERE ID = ? ";
private static final String QUERYCALL_SQL = " SELECT CALLS from BS_T_WF4_FTTH_TOINTERFACE WHERE ID = ?" ;
private static final String QUERYPORT_SQL = " SELECT * from BS_T_WF4_FTTH WHERE ORDERID=? AND RESULTTYPECODE=? AND FLAG=4";
private static final String BACKPORT_SQL = " UPDATE BS_T_WF4_FTTH_TOINTERFACE SET FLAG = 10,RECORDTIME = ? WHERE ORDERID=? AND RESULTTYPECODE=?";
private static final String OLDOTV_SQL = "select * from dw_order_fttx_otv where pid=?";
private static final String RESP_SUCCESS = "0000";// xml驗證成功
private DataAdapter dataAdapter = new DataAdapter();
private QueryAdapter queryAdapter = new QueryAdapter();
private String threadid = "";
@Override
public void run() {
while (true) {
try {
//System.out.println("<----OldRadius start, Thread id:"+this.getThreadid()+" currentTime:"+new Date()+"------->");
sleep(1000 * 10 * 1);
try {
dealData();
} catch (Exception e) {
e.printStackTrace();
}
//System.out.println("<-----OldRadius end, Thread id:"+this.getThreadid()+" currentTime:"+new Date()+"------->");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
}
}
}
public void dealData(){
HashMap<String, String> p_indirectValues = new HashMap<String, String>();
Long currentTime = TimeUtils.getCurrentTime();
p_indirectValues.put("flag", "10");
p_indirectValues.put("calls", "1");
p_indirectValues.put("recordtime", String.valueOf(currentTime));
p_indirectValues.put("threadid", threadid);
RQuery4NoLogs rquery = new RQuery4NoLogs("SQL_Thread_Eoms2Prov4OldRadius.query", p_indirectValues);
//QueryAdapter qa = new QueryAdapter();
QueryAdapter4NoLogs qa=new QueryAdapter4NoLogs();
DataTable dt = rquery.getDataTable4Nologs();
List<DataRow> list = dt.getRowList();
DataRow row =null;
String wsMethod = null;
String baseSchema = null;
String resultTypeCode = null;
String baseId = null;
Long currenttime = 0l;
Long endtime = 0l;
for(int i = 0; i < list.size(); i++){
currenttime = System.currentTimeMillis();
row = list.get(i);
wsMethod = "checkinWorkSheet";
baseSchema = row.getString("baseschema");
resultTypeCode = row.getString("resulttypecode");
baseId = row.getString("baseid");
if("RADIUSFILE".equals(resultTypeCode)){ //裝機ONU回收
wsMethod = "queryUserSession";
}
String result = "";
String account_number = "";
String timeStr = TimeUtils.getCurrentDate("yyyyMMddHHmmss");
String Sql = "select BI_CUSTACCOUNT,ISSCHOOLCOUNT from dw_order_fttx_bf where pid=?";
DataTable accountdt = qa.executeQuery(Sql, new Object[]{baseId});
if(accountdt!=null && accountdt.length()>0){
account_number = accountdt.getDataRow(0).getString("BI_CUSTACCOUNT");
String isschool = accountdt.getDataRow(0).getString("ISSCHOOLCOUNT");
if(isschool!=null && !"".equals(isschool)){
account_number = account_number+isschool;
}
}
//LogRecord.info("賬號:"+account_number);
String newWsAddress=WsUtil.getWsAddress("EL_FTTH_BF", "queryUserSession", "A", "B");
try {
result= this.invokeAccess(newWsAddress, account_number);
} catch (Exception e) {
//異常返回
//System.out.println("<------the Access is destroyed!------->");
this.dealExceptionResult(row);
}
//正常返回
this.dealResult(row, result);
endtime = System.currentTimeMillis();
// System.out.println("thread id:"+threadid+",order type:"+baseSchema+",order pid:"+baseId +",access type:"+resultTypeCode+"times:"+(endtime-currenttime));
}
}
SQL查詢:
<sqlquery name="SQL_Thread_Eoms2Prov4OldRadius.query"><!-- Eoms2Prov 介面資料查詢 集中處理查詢失敗的 -->
<select>
<![CDATA[
SELECT /*COUNT*/ * /*COUNT*/ FROM (
SELECT
t.*,
mod(RECORDTIME,10) threadid
FROM BS_T_WF4_FTTH_TOINTERFACE t
WHERE RESULTTYPECODE ='RADIUSFILE'
$customwhere$
) WHERE ROWNUM <100 $customwhere1$
/*ORDERBY*/ ORDER BY /*ORDERBY*/ RECORDTIME ASC
]]>
</select>
<customwhere name="customwhere" prepend="and" >
<field prepend="and" operator="=" colname="flag" value="#flag#"/>
<field prepend="and" operator=">" colname="calls" value="#calls#"/>
<field prepend="and" operator="<" colname="recordtime" value="#recordtime#"/>
</customwhere>
<customwhere name="customwhere1" prepend="and" >
<field prepend="threadid" operator="=" colname="threadid" value="#threadid#"/>
</customwhere>
</sqlquery>
這裡沒有直接在main方法中new 執行緒,而且採用配置表的方式,通過java反射機制動態的生成執行緒物件並執行。主要是為了後期的多執行緒智慧化管理
Thread管理類:
主要功能:完成對多種執行緒的建立與啟動
package com.ultrapower.eoms.common.thread;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import net.sf.json.JSONObject;
import com.ultrapower.eoms.common.constants.PropertiesUtils;
import com.ultrapower.eoms.common.core.component.data.DataAdapter;
import com.ultrapower.eoms.common.core.component.data.DataRow;
import com.ultrapower.eoms.common.core.component.data.DataTable;
import com.ultrapower.eoms.common.core.component.data.QueryAdapter;
import com.ultrapower.eoms.common.core.util.TimeUtils;
import com.ultrapower.eoms.common.core.util.UUIDGenerator;
/**
* 執行緒智慧化管理
*
*/
public class ThreadManageAI {
private static String projectInfo = null;
static Map<String,Thread> threadMap = new HashMap<String,Thread>();
/**
* 獲取執行緒SQL
*/
String SQL_GET_THREADINFO_INDB = "SELECT * FROM BS_T_SM_THREADMANAGE WHERE SERVERINFO=? AND THREADFLAG=1 ORDER BY THREADNAME";
QueryAdapter queryAdapter = new QueryAdapter();
DataAdapter dataAdapter = new DataAdapter();
/**
* 執行緒監聽
*/
public void threadMonitor(){
if(projectInfo==null || "".equals(projectInfo)){
projectInfo = PropertiesUtils.getProperty("ThreadManageAI.projectInfo");
}
DataTable logdt = new DataTable("BS_T_SM_THREADMANAGE_LOG");
DataRow logdr = null;
if(projectInfo!=null && !"".equals(projectInfo)){
DataTable dt = queryAdapter.executeQuery(SQL_GET_THREADINFO_INDB, projectInfo);
if(dt!=null && dt.length()>0){
DataRow dr = null;
String threadName = null;
String clazz = null;
String paramJsonStr = null;
String logDesc = null;
boolean isAlive = false;
for(int i=0,j=dt.length();i<j;i++){
isAlive = false;
dr = dt.getDataRow(i);
threadName = dr.getString("THREADNAME");
threadName = projectInfo+"."+threadName;
Thread t = threadMap.get(threadName);
if(t!=null){
isAlive = t.isAlive();
}else{
isAlive = false;
}
if(!isAlive){//需要重啟執行緒
//重啟執行緒
paramJsonStr = dr.getString("PARAMJSON");//獲取執行緒引數
clazz = dr.getString("CLAZZ");//執行緒類
Class XXthreadClass = null;
try {
XXthreadClass = Class.forName(clazz);
} catch (ClassNotFoundException e) {
XXthreadClass = null;
e.printStackTrace();
}
if(XXthreadClass!=null){
Object xxthread = null;
try {
xxthread = XXthreadClass.newInstance();
} catch (InstantiationException e) {
xxthread = null;
e.printStackTrace();
} catch (IllegalAccessException e) {
xxthread = null;
e.printStackTrace();
}
if(xxthread!=null){
boolean isCanStart = true;//預設可以啟動執行緒
if(paramJsonStr!=null && !"".equals(paramJsonStr)){
JSONObject paramJson = JSONObject.fromObject(paramJsonStr);
Method method = null;
if(paramJson==null||paramJson.size()==0){
//引數不為空,但是有錯誤,不能啟動
logDesc = "引數不為空,但是有錯誤,不能啟動";
isCanStart = false;
}else{
try {
Set<String> keys = paramJson.keySet();
for(String key:keys){
//例子,引數名threadid,方法名:setThreadid(String threadid)
String methodName = "set"+key.substring(0,1).toUpperCase()+key.substring(1);
method = XXthreadClass.getMethod(methodName, String.class);
try {
method.invoke(xxthread, paramJson.getString(key));
} catch (IllegalArgumentException e) {
isCanStart = false;
e.printStackTrace();
} catch (IllegalAccessException e) {
isCanStart = false;
e.printStackTrace();
} catch (InvocationTargetException e) {
isCanStart = false;
e.printStackTrace();
}
}
} catch (SecurityException e) {
isCanStart = false;
e.printStackTrace();
} catch (NoSuchMethodException e) {
isCanStart = false;
e.printStackTrace();
}
}
}
if(isCanStart){
boolean isStarted = true;
try {
Method startMethod = XXthreadClass.getMethod("start");
try {
startMethod.invoke(xxthread);
} catch (IllegalArgumentException e) {
isStarted = false;
e.printStackTrace();
} catch (IllegalAccessException e) {
isStarted = false;
e.printStackTrace();
} catch (InvocationTargetException e) {
isStarted = false;
e.printStackTrace();
}
} catch (SecurityException e) {
isStarted = false;
e.printStackTrace();
} catch (NoSuchMethodException e) {
isStarted = false;
e.printStackTrace();
}
if(isStarted){
t = (Thread)xxthread;
threadMap.put(threadName,t);
//執行緒啟動成功
logDesc = "執行緒啟動成功";
}else{
//執行緒啟動失敗
logDesc = "執行緒啟動失敗";
}
}else{
//引數設定失敗
logDesc = "引數設定失敗";
}
}else{
//執行緒無法例項化
logDesc = "執行緒無法例項化";
}
}else{
//CLAZZ有誤,無法載入類
logDesc = "CLAZZ有誤,無法載入類";
}
}else{
//執行緒存在,列印日誌
logDesc = "執行緒還存活";
}
HashMap<String,Object> rowMap = dr.getRowHashMap();
logdr = new DataRow();
for(String key:rowMap.keySet()){
Object value = rowMap.get(key);
if(value!=null){
logdr.put(key, value);
}
}
logdr.put("PID", UUIDGenerator.getUUIDoffSpace());
logdr.put("LOGTIME", TimeUtils.getCurrentTime());
logdr.put("LOGDESC", logDesc);
logdt.putDataRow(logdr);
}
}else{
logdr = new DataRow();
logdr.put("PID", UUIDGenerator.getUUIDoffSpace());
logdr.put("LOGTIME", TimeUtils.getCurrentTime());
logdr.put("LOGDESC", "沒有需要啟動的執行緒");
logdr.put("SERVERINFO", projectInfo);
logdt.putDataRow(logdr);
}
}else{
logdr = new DataRow();
logdr.put("PID", UUIDGenerator.getUUIDoffSpace());
logdr.put("LOGTIME", TimeUtils.getCurrentTime());
logdr.put("LOGDESC", "projectInfo為空,沒有需要啟動的執行緒");
logdt.putDataRow(logdr);
}
dataAdapter.executeAdd(logdt);
}
}
這樣,我們只需要在目標表中配置執行緒ID 、執行緒的業務型別、執行緒實現類的全路徑,以及入參,就可以通過反射的方式建立並啟動多個執行緒,就這樣,竣工歸檔校驗功能第二版改造到一段落。通過運維人員反映,部分解決了工單由於等待太久而阻塞的問題。
然後在系統正常執行大概有一個月後,運維人員又一次發現了一個重大問題,那就是有很多裝機工單是一直查不到上網記錄,不斷的進入佇列,導致新工單本來可以歸檔的工單卡在了竣工校驗環節。
通過查詢日誌並定位,又重新擬定了一套方案,就是分組處理工單,即一部分是處理沒校驗通過的老工單,另一部分則是處理新流轉進來的工單。這樣就有效解決了新工單不至於在處理佇列中等待太久。
程式碼略___
專案場景分析(二)
背景:同樣是重慶移動代維綜合管理系統,web端大致可以分為 兩部分 業務系統和流程引擎,其中業務系統主要處理的是各類工單,各種人員,資源以及代維公司和分公司的管理;但往往完成一個業務場景是需要多人協助參與的,所以就引入了工作流這個概念,但往往工作流底層是比較複雜的;流程引擎恰恰就是基於工作流的思想進行了封裝和改造,使得開發人員可以更加簡潔的新建、修改、啟動一個自定義的流程。而由於是是多人蔘與,那麼就會涉及到人與人,人與系統之間的通訊,基於人性化考慮,我們決定採用發簡訊的方式推送給工單待辦人。
遇到的問題: 由於代維繫統本身不提供發簡訊的服務,因此必須藉助第三方系統,即通過呼叫第三方介面的方式,將代維繫統的業務資訊推送給代辦人;那麼問題來了,既然是調介面,那麼肯定涉及到時延的問題,這樣就存在主執行緒必須等待介面返回結果後才能往下執行,而往往傳送簡訊是一個比較耗時的過程。
解決方案: 因此我們想到採用多執行緒非同步的方式呼叫簡訊平臺介面,這樣既不阻塞主執行緒執行,也給使用者帶來了良好的體驗。程式碼如下:
package com.ultrapower.mams.smsnew.utils;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.ultrapower.eoms.common.core.component.data.DataAdapter;
import com.ultrapower.eoms.common.core.component.data.DataRow;
import com.ultrapower.eoms.common.core.component.data.DataTable;
import com.ultrapower.eoms.common.core.util.TimeUtils;
import com.ultrapower.eoms.common.core.util.UUIDGenerator;
import com.ultrapower.mams.smsnew.constants.SmsNewConstants;
import com.ultrapower.mams.smsnew.thread.SmsSendOnceThread;
public class SmsNewUtil {
public static DataAdapter dataAdapter = new DataAdapter();
/**
*
* @param smsType 業務分類
* @param relateId 業務關聯ID
* @param content 簡訊內容
* @param mobile 簡訊接收號碼
* @param PRIORITY 優先順序,1/2/3,優先順序一次降低(對於時效性較高的簡訊才使用1級,一般簡訊使用2,無時效性要求的使用3)
* @param maxSendNum 最大發送次數
* @param preSendTime 定時傳送時間
* @param remark 備註
* @return
*/
public
相關推薦
多執行緒系列之 java多執行緒的個人理解(二)
前言:上一篇多執行緒系列之 java多執行緒的個人理解(一) 講到了執行緒、程序、多執行緒的基本概念,以及多執行緒在java中的基本實現方式,本篇主要接著上一篇繼續講述多執行緒在實際專案中的應用以及遇到的諸多問題和解決方案
深入理解系列之JAVA多執行緒(2)——synchronized同步原理
多執行緒中為了解決執行緒安全問題,一個重要的手段就是同步!所謂同步其實就是使得原本各個執行緒交叉執行(非同步),變成排隊執行(同步)。同步策略使得不同執行緒操作共享資料遵循“先來後到“,從而避免某個執行緒沒有處理完資料就被另一執行緒搶佔操作出現資料被覆蓋或
深入理解系列之JAVA多型機制(過載/重寫)
多型(Polymorphism)按字面的意思就是“多種狀態”。在面嚮物件語言中,介面的多種不同的實現方式即為多型(來自百度百科)。所以,按照這個意思其實多型的“主題”是物件,但是實際在我們運用中我們常把“過載”和“重寫”稱之為“多型”其實這是不嚴謹的!過載
java多執行緒系列之模式|第一篇-Guarded Suspension pattern
Guarded Suspension pattern模式
作者注:該系列文章基於《java執行緒設計模式》撰寫,只用於學習和交流。
定義:多執行緒執行,當前執行緒沒有達到警戒條件時,執行緒會進入等待直到
java多執行緒系列之模式|第三篇: Producer-Consumer pattern
生產者-消費者模式
含義:顧名思義,生產者用來生產資料,可能有一到多個,消費者用來消費資料,也可能有多個,中間會有一個“橋樑參與者”,作為資料的存放以及執行緒之間的同步和協調。
範例程式行為:
廚師(MakerThread)做蛋糕,做好後放在桌子(Table)上
桌子
面試題之---java多執行緒
(一)多執行緒1,概念介紹 一般一個應用至少一個程序,一個執行緒,執行緒是程序的一個實體,是CPU排程和分派的基本單位.最簡單的比喻多執行緒就像火車的每一節車廂,而程序則是火車。車廂離開火車是無法跑動的,同理火車也不可能只有一節車廂。 在作業系統中,執行緒是最小的排
多執行緒系列之——事件核心物件
所有核心物件裡面事件核心物件是最簡單的一個,它包括一個使用計數,還有兩個布林值。一個布林值用來表示事件是手動重置事件還是自動重置事件,另一個布林值表示當前是否處於觸發狀態。
當一個手動重置事件被觸發的時候,所有等待該事件的執行緒都能變成排程狀態。而一個自動重置事件被觸發
[計算機領域的思維導圖系列整理][java]多執行緒
本圖整理來源於關注java多執行緒的博主hacke2,原作者部落格在此請點選,如果有相關的連結,請大家告訴小編哈。
hacke2
多執行緒初級
多執行緒 中級
多執行緒中級,包含控制執行緒的幾種方法、執行緒的同步、執行緒組,有返回值的執行緒、執行
Java之執行緒一(Java多執行緒程式設計核心技術)
一、等待/通知機制的實現1、wait()的作用是使當前執行程式碼的執行緒進行等待,將當前執行緒放入‘預執行佇列’中,並且在wait()所在的程式碼處停止執行,直到接到通知或者中斷為止。注意:在呼叫wait()之前,執行緒必須獲得該物件的物件級別鎖,所以只能在同步方法或者同步塊
Java 多執行緒程式設計之“兩個執行緒實現一個執行緒列印奇數,另一個執行緒列印偶數”
題目:t從0到N,一個執行緒列印奇數,一個執行緒列印偶數,按順序打印出來。
最終列印結果:0,1,2,3,4,...,N;
思路:兩個執行緒間的通訊採用等待,喚醒方法——列印奇偶數由flag控制,當flag為真時列印偶數;
列
Android進階——多執行緒系列之非同步任務AsyncTask的使用與原始碼分析
AsyncTask是什麼
AsyncTask是一種輕量級的非同步任務類,它可以線上程池中執行後臺任務,然後把執行的進度和最終結果傳遞給主執行緒並主執行緒中更新UI,通過AsyncTask可以更加方便執行後臺任務以及在主執行緒中訪問UI,但是AsyncTask並
Thread執行緒系列之多執行緒下載
瞭解了這麼多與執行緒相關的知識,那麼我們也要實戰一下了(在學習本篇知識之前,如果對java中的網路基礎連結不太熟悉的,建議先去學一下java網路程式設計,再來看本文章。)因為本篇是多執行緒下載的demo,所以就直接附上程式碼,裡面都寫好了註釋,不影響對本篇的學習。packag
Android進階——多執行緒系列之wait、notify、sleep、join、yield、synchronized關鍵字、ReentrantLock鎖
前言
多執行緒一直是初學者最困惑的地方,每次看到一篇文章,覺得很有難度,就馬上叉掉,不看了,我以前也是這樣過來的。後來,我發現這樣的態度不行,知難而退,永遠進步不了。於是,我狠下心來看完別人的部落格,儘管很難但還是咬著牙,不懂去查閱資料,到最後弄懂整個過程。雖
Java總結篇系列:Java多執行緒(四)
多個執行緒同步執行ping ip示例package com.ebao.pojo;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import j
多執行緒同步之——兩個執行緒序列順序列印奇數和偶數的兩種實現
題目:一道經典的執行緒併發的問題,執行緒a列印1、3、5……,執行緒b列印2、4、6……,兩個執行緒交替執行輸出1、2、3、4、5、6……
要點:
package com.test;
import java.util.concurrent.locks.
-1-5 java 多執行緒 概念 程序 執行緒區別聯絡 java建立執行緒方式 執行緒組 執行緒池概念 執行緒安全 同步 同步程式碼塊 Lock鎖 sleep()和wait()方法的區別 為什麼wait(),notify(),notifyAll()等方法都定義在O
本文關鍵詞:
java 多執行緒 概念 程序 執行緒區別聯絡 java建立執行緒方式 執行緒組 執行緒池概念 執行緒安全 同步 同步程式碼塊 Lock鎖 sleep()和wait()方法的區別 為什麼wait(),notify(),notifyAll()等方法都定義在Object類中
多執行緒
[C++ 2011 多執行緒系列一]如何建立執行緒
/*
thread.cpp
演示了建立執行緒的多種方法
*/
#include <iostream>
#include <sstream>
#include <functional>
#include <thread>
#i
死磕 java執行緒系列之執行緒模型
(2)執行緒模型有哪些?
(3)各語言使用的是哪種執行緒模型?
簡介
在Java中,我們平時所說的併發程式設計、多執行緒、共享資源等概念都是與執行緒相關的,這裡所說的執行緒實際上應該叫作“使用者執行緒”,而對應到作業系統,還有另外一種執行緒叫作“核心執行緒”。
使用者執行緒位於核心之上,它的管理無需核心支援
死磕 java執行緒系列之建立執行緒的8種方式
問題
(1)建立執行緒有哪幾種方式?
(2)它們分別有什麼運用場景?
簡介
建立執行緒,是多執行緒程式設計中最基本的操作,彤哥總結了一下,大概有8種建立執行緒的方式,你知道嗎?
繼承Thread類並重寫run()方法
public class CreatingThread01 extends Thread
死磕 java執行緒系列之自己動手寫一個執行緒池
歡迎關注我的公眾號“彤哥讀原始碼”,檢視更多原始碼系列文章, 與彤哥一起暢遊原始碼的海洋。
(手機橫屏看原始碼更方便)
問題
(1)自己動手寫一個執行緒池需要考慮哪些因素?
(2)自己動手寫的執行緒池如何測試?
簡介
執行緒池是Java併發程式設計中經常使用到的技術,那麼自己如何動手寫一個執行緒池呢?本