接下來,我們說 feature模式跟master-worker 模式,另外還有個知識點,Executors框架的一系列問題。
feature模式:之前稍微提到了一點,現在用代碼細細實現下。所謂的feature模式,我的理解就是,當前用戶執行一個任務,這個任務主線程執行時間很長很長,類似於財務系統的結算,這時候呢,我們首先讓用戶看見程序的執行結果(當然可以是僞造的),然後後臺悄無聲息的啓動N個線程,分批的去執行用戶剛纔提交的那個任務,等到客戶想看真正的數據的時候,這時候,多線程可能就執行完了,沒執行完的話,就讓用戶wait,不多說,看僞代碼
public classMain {
public static void main(String[] args) {
FeatureClient fc = new FeatureClient();
Data data = fc.request("請求");
System.out.println("請求成功");
List<RealData> msg = data.getRequest();
}
}
首先呢,fc類似於request的請求,data數據是用戶想執行看到的結果,fc.request()。往下看這個方法
public class FeatureClient {
public Data request(final String m) {
final FeatureData featureData = new FeatureData();
new Runnable() {
public void run() {
List<RealData> realData = new ArrayList<RealData>();
//數據庫操作realData
featureData.setRequest(realData);
}
};
return featureData;
}
}
內部類裏面啓用了一個線程,去執行用戶的請求,主函數執行返回僞結果,
public class FeatureClient {
public Data request(final String m) {
final FeatureData featureData = new FeatureData();
new Runnable() {
public void run() {
List<RealData> realData = new ArrayList<RealData>();
//數據庫操作realData
featureData.setRequest(realData);
}
};
return featureData;
}
}
public interface Data {
List<RealData> getRequest();
}
public class RealData {
}
這是setData的代碼,feature模式就是這麼簡單。
-------------------------------
下面說下master-worker模式。
master-worker講的是什麼事呢,就是說,舉個場景,當前有個消息隊列,隊列裏面全都是任務,worker是打工者,需要執行這些任務,
master類似於一箇中樞軸的概念,專門添加任務和解析任務吧,負責統計一些事情。這裏需要用到concurrentMap這個保證線程安全的類
,還有個LinkedBlockingQueue,專門負責裝載任務,下面看代碼,
public class Main { public static void main(String[] args) { Worker worker = new Worker(); Master master = new Master(worker, 10); for (int i = 1; i <= 100; i++) { Task task = new Task(); task.setId(i); task.setPrice(new Random().nextInt(1000)); master.submit(task); } master.execute(); Long time = System.currentTimeMillis(); while (master.isComplete()) { Long time1 = System.currentTimeMillis() - time; System.out.println(time1); break; } } }當前呢,有兩個類Worker,Master。我初始化了100個任務,需要用到master的添加任務的方法,放到Queue裏面,
public class Master {
private ConcurrentLinkedQueue<Task> workQueue = new ConcurrentLinkedQueue<Task>();
private HashMap<String, Thread> workers = new HashMap<String, Thread>();
private ConcurrentHashMap<String, Object> resultMap = new ConcurrentHashMap<String, Object>();
public Master(Worker worker, int workerCount) {
worker.setWorkQueue(workQueue);
worker.setResultMap(resultMap);
for (int i = 0; i < workerCount; i++) {
workers.put("子節點" + Integer.toString(i), new Thread(worker));
}
}
public void submit(Task task) {
workQueue.add(task);
}
public void execute() {
for (Map.Entry<String, Thread> me : workers.entrySet()) {
me.getValue().start();
}
}
public boolean isComplete() {
for (Map.Entry<String, Thread> me : workers.entrySet()) {
if (me.getValue().getState() != Thread.State.TERMINATED) {
return false;
}
}
return true;
}
}
Master裏面有添加任務的方法(submit),有判斷任務是否執行完畢(isComplete),有初始化執行任務的方法,然後呢,再看master的構造方法,兩個傳參,worker對象
和數量,有個hashMap,專門記錄當前是哪個Thread。然後再看,Worker類代碼,
public class Worker implements Runnable {
private ConcurrentLinkedQueue<Task> workQueue = new ConcurrentLinkedQueue<Task>();
private ConcurrentHashMap<String, Object> resultMap = new ConcurrentHashMap<String, Object>();
public void run() {
while (true) {
Task task = this.workQueue.poll();
if (task == null) {
break;
}
resultMap.put(Integer.toString(task.getId()), task);
}
}
public void setWorkQueue(ConcurrentLinkedQueue<Task> workQueue) {
this.workQueue = workQueue;
}
public void setResultMap(ConcurrentHashMap<String, Object> resultMap) {
this.resultMap = resultMap;
}
}
裏面有個實現了Runnable的方法,在run裏面執行了while,我解釋下poll方法,poll方法的意思是,取出當前Queue隊列的第一個元素並刪除,這裏的僞代碼,我簡單的打印了id,(當然,這裏可以統計Task類裏面的屬性,執行++操作。),把結果放到了concurrentMap的結果集裏面。再看Main類,Master的構造函數,我初始化了worker對象,10個線程去執行,100個任務,直到Queue裏面的任務執行完畢,返回到concurrentMap的結果集裏面去。這就是Master-worker模式(需要掌握精髓)。實在不理解的可以動手寫寫代碼,去debug下。好記性不如爛筆頭。
----------------------------
最後一個知識點,Executors框架。
Executors框架 : Executors類,提供了一系列工廠方法用於創先線程池,返回的線程池都實現了ExecutorService接口。比如
newFixedThreadPool,newSingleThreadExecutor,newCachedThreadPool,newScheduledThreadPool.這四個方法jdk實現機制都是
ThreadPoolExecutor,利用了工廠方法,根據傳入參數的不同,去實例化不同的ThreadPool對象。下面簡單說下ThreadPoolExecutor的構
造、方法的各個參數。
corePoolSize, 核心線程數,
maximumPoolSize,最大線程數,
keepAliveTime,存活時間,
unit,存活時間的單位,
workQueue,需要放任務的隊列,
RejectedExecutionHandler,放棄操作。
子,
public class UseThreadExecutorPool implements Runnable {
private static AtomicInteger count = new AtomicInteger();
@Override
public void run() {
int all = count.incrementAndGet();
System.out.println(all);
}
//自定義線程池 各個參數什麼意思 corePoolSize 核心線程數,maximumPoolSize 最大線程數,
//當 任務數量 <= corePoolSize 直接執行, 當 maximumPoolSize =>任務數量 > corePoolSize ,任務數量-corePoolSize 的任務 加到queue裏面,
//拿corePoolSize 線程執行,
//當 maximumPoolSize < 任務數量 在不超過 maximumPoolSize的情況下 新建線程執行 加滿queue之後的 任務,當超過了最大線程數,就報錯,
//執行勸退側洛
public static void main(String[] args) {
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<UseThreadExecutorPool>(4);
ExecutorService executorService = new ThreadPoolExecutor(5, 10, 60, TimeUnit.DAYS, arrayBlockingQueue);
UseThreadExecutorPool useThreadExecutorPool0 = new UseThreadExecutorPool();
UseThreadExecutorPool useThreadExecutorPool1 = new UseThreadExecutorPool();
//UseThreadExecutorPool useThreadExecutorPool2 = new UseThreadExecutorPool();
//UseThreadExecutorPool useThreadExecutorPool3 = new UseThreadExecutorPool();
//UseThreadExecutorPool useThreadExecutorPool4 = new UseThreadExecutorPool();
//UseThreadExecutorPool useThreadExecutorPool5 = new UseThreadExecutorPool();
executorService.execute(useThreadExecutorPool0);
executorService.execute(useThreadExecutorPool1);
//executorService.execute(useThreadExecutorPool2);
//executorService.execute(useThreadExecutorPool3);
//executorService.execute(useThreadExecutorPool4);
//executorService.execute(useThreadExecutorPool5);
executorService.shutdown();
}
}
大家都知道,ArrayBlockingQueue是有界隊列,LinkedBlockingQueue是無界隊列(當然可以變成有界),看上面的代碼,new ThreadPoolExecutor的構造參數,5代表,我初始化5個線程,10代表, 支持最大10個線程,60呢代表,這些線程的空閒時間,當然得有單位啦,下個參數就是60的參數,可以是天,可以是秒,也可以是小時,再下一個參數就是,Queue類的,這裏的Queue可以是上面隊列的任何一個,但是代表的意義都不一樣。Queue後面其實還有個參數,叫做勸退策略,專門負責隊列滿了,剩下的任務也超過了最大線程數,就會執行勸退策略,jdk有默認的,所以不寫也行,當然也可以自己寫,自己寫的話,必須要實現RejectedExecutionHandler接口
jdk提供了四種勸退策略,
AbortPolicy:直接拋出異常組織系統正常工作,
CallerRunsRolicy:只要線程池未關閉,該側羅直接在調用者線程中,運行當前被丟棄的任務,
DiscardOldestPolicy:丟棄最老的一個請求,嘗試再次提交當前任務,
DiscardPolicy:丟棄無法處理的任務,不給於任何處理。
上面的代碼什麼意思呢,稍後的博客會帶來更詳細的分析。今天的知識點,可以說有點難度,需要好好的理解理解,當然,我說的也不太好,儘量讓大家明白。
題外話:多線程這個東西吧,面試經常考,但是呢,絕大公司技術是不會用到的,多線程呢,屬於java基礎,出去面試你想要更高的工資,20k,25k,更有30k的,面試官說了一個知識點(當然,面試官也可能只是懂點皮毛),你不知道,那你的身價可是大大打了折扣,如果你瞭解的更深入底層的代碼的話,能說出來,那你在面試官心裏的感覺就不一樣了(工資也好要了),這只是個人的理解。也希望下回各位小夥伴跳槽工資漲的最少5000塊吧、有興趣的可以加我QQ :82479297 我也是個技術成長道路上的小白,
可以交流交流。下次聊。