多線程(二)

接下來,我們說 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,放棄操作。


下面介紹下我理解的參數的意義,corePoolSize,核心線程數,就是初始化的線程數,maximumPoolSize,最大線程數,這兩者的區別需要根據傳入的Queue去介紹,舉個例

子,

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  我也是個技術成長道路上的小白,

可以交流交流。下次聊。

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