java併發編程(2)線程池的使用

来源:http://www.cnblogs.com/zhangxinly/archive/2017/05/26/6907186.html
-Advertisement-
Play Games

一、任務和執行策略之間的隱性耦合 Executor可以將任務的提交和任務的執行策略解耦 只有任務是同類型的且執行時間差別不大,才能發揮最大性能,否則,如將一些耗時長的任務和耗時短的任務放在一個線程池,除非線程池很大,否則會造成死鎖等問題 1.線程饑餓死鎖 類似於:將兩個任務提交給一個單線程池,且兩個 ...


一、任務和執行策略之間的隱性耦合

  Executor可以將任務的提交和任務的執行策略解耦

  只有任務是同類型的且執行時間差別不大,才能發揮最大性能,否則,如將一些耗時長的任務和耗時短的任務放在一個線程池,除非線程池很大,否則會造成死鎖等問題

1.線程饑餓死鎖

  類似於:將兩個任務提交給一個單線程池,且兩個任務之間相互依賴,一個任務等待另一個任務,則會發生死鎖;表現為池不夠

  定義:某個任務必須等待池中其他任務的運行結果,有可能發生饑餓死鎖

2.線程池大小

  

  註意:線程池的大小還受其他的限制,如其他資源池:資料庫連接池

    如果每個任務都是一個連接,那麼線程池的大小就受制於資料庫連接池的大小

3.配置ThreadPoolExecutor線程池

實例:

  1.通過Executors的工廠方法返回預設的一些實現

  2.通過實例化ThreadPoolExecutor(.....)自定義實現

線程池的隊列

  1.無界隊列:任務到達,線程池飽滿,則任務在隊列中等待,如果任務無限達到,則隊列會無限擴張

    如:單例和固定大小的線程池用的就是此種

  2.有界隊列:如果新任務到達,隊列滿則使用飽和策略

    3.同步移交:如果線程池很大,將任務放入隊列後在移交就會產生延時,如果任務生產者很快也會導致任務排隊

    SynchronousQueue直接將任務移交給工作線程

    機制:將一個任務放入,必須有一個線程等待接受,如果沒有,則新增線程,如果線程飽和,則拒絕任務

    如:CacheThreadPool就是使用的這種策略

飽和策略:

  setRejectedExecutionHandler來修改飽和策略

  1.終止Abort(預設):拋出異常由調用者處理

  2.拋棄Discard

  3.拋棄DiscardOldest:拋棄最舊的任務,註意:如果是優先順序隊列將拋棄優先順序最高的任務

  4.CallerRuns:回退任務,有調用者線程自行處理

4.線程工廠ThreadFactoy

   每當創建線程時:其實是調用了線程工廠來完成

   自定義線程工廠:implements ThreadFactory

   可以定製該線程工廠的行為:如UncaughtExceptionHandler等

  

public class MyAppThread extends Thread {
    public static final String DEFAULT_NAME = "MyAppThread";
    private static volatile boolean debugLifecycle = false;
    private static final AtomicInteger created = new AtomicInteger();
    private static final AtomicInteger alive = new AtomicInteger();
    private static final Logger log = Logger.getAnonymousLogger();

    public MyAppThread(Runnable r) {
        this(r, DEFAULT_NAME);
    }

    public MyAppThread(Runnable runnable, String name) {
        super(runnable, name + "-" + created.incrementAndGet());
        //設置該線程工廠創建的線程的 未捕獲異常的行為
        setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread t,
                                          Throwable e) {
                log.log(Level.SEVERE,
                        "UNCAUGHT in thread " + t.getName(), e);
            }
        });
    }

    public void run() {
        // Copy debug flag to ensure consistent value throughout.
        boolean debug = debugLifecycle;
        if (debug) log.log(Level.FINE, "Created " + getName());
        try {
            alive.incrementAndGet();
            super.run();
        } finally {
            alive.decrementAndGet();
            if (debug) log.log(Level.FINE, "Exiting " + getName());
        }
    }

    public static int getThreadsCreated() {
        return created.get();
    }

    public static int getThreadsAlive() {
        return alive.get();
    }

    public static boolean getDebug() {
        return debugLifecycle;
    }

    public static void setDebug(boolean b) {
        debugLifecycle = b;
    }
}

 

5.擴展ThreadPoolExecutor

  可以被自定義子類覆蓋的方法:

  1.afterExecute:結束後,如果拋出RuntimeException則方法不會執行

  2.beforeExecute:開始前,如果拋出RuntimeException則任務不會執行

  3.terminated:線上程池關閉時,可以用來釋放資源等

 

二、遞歸演算法的並行化

1.迴圈  

  在迴圈中,每次迴圈操作都是獨立的

//串列化
    void processSequentially(List<Element> elements) {
        for (Element e : elements)
            process(e);
    }
    //並行化
    void processInParallel(Executor exec, List<Element> elements) {
        for (final Element e : elements)
            exec.execute(new Runnable() {
                public void run() {
                    process(e);
                }
            });
    }

 

2.迭代

    如果每個迭代操作是彼此獨立的,則可以串列執行

  如:深度優先搜索演算法;註意:遞歸還是串列的,但是,每個節點的計算是並行的

  

//串列 計算compute 和串列迭代
    public <T> void sequentialRecursive(List<Node<T>> nodes, Collection<T> results) {
        for (Node<T> n : nodes) {
            results.add(n.compute());
            sequentialRecursive(n.getChildren(), results);
        }
    }
    //並行 計算compute 和串列迭代
    public <T> void parallelRecursive(final Executor exec, List<Node<T>> nodes, final Collection<T> results) {
        for (final Node<T> n : nodes) {
            exec.execute(() -> results.add(n.compute()));
            parallelRecursive(exec, n.getChildren(), results);
        }
    }
    //調用並行方法的操作
    public <T> Collection<T> getParallelResults(List<Node<T>> nodes)
            throws InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool();
        Queue<T> resultQueue = new ConcurrentLinkedQueue<T>();
        parallelRecursive(exec, nodes, resultQueue);
        exec.shutdown();
        exec.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
        return resultQueue;
    }

 

  實例:

  

public class ConcurrentPuzzleSolver <P, M> {
    private final Puzzle<P, M> puzzle;
    private final ExecutorService exec;
    private final ConcurrentMap<P, Boolean> seen;
    protected final ValueLatch<PuzzleNode<P, M>> solution = new ValueLatch<PuzzleNode<P, M>>();

    public ConcurrentPuzzleSolver(Puzzle<P, M> puzzle) {
        this.puzzle = puzzle;
        this.exec = initThreadPool();
        this.seen = new ConcurrentHashMap<P, Boolean>();
        if (exec instanceof ThreadPoolExecutor) {
            ThreadPoolExecutor tpe = (ThreadPoolExecutor) exec;
            tpe.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
        }
    }

    private ExecutorService initThreadPool() {
        return Executors.newCachedThreadPool();
    }

    public List<M> solve() throws InterruptedException {
        try {
            P p = puzzle.initialPosition();
            exec.execute(newTask(p, null, null));
            // 等待ValueLatch中閉鎖解開,則表示已經找到答案
            PuzzleNode<P, M> solnPuzzleNode = solution.getValue();
            return (solnPuzzleNode == null) ? null : solnPuzzleNode.asMoveList();
        } finally {
            exec.shutdown();//最終主線程關閉線程池
        }
    }

    protected Runnable newTask(P p, M m, PuzzleNode<P, M> n) {
        return new SolverTask(p, m, n);
    }

    protected class SolverTask extends PuzzleNode<P, M> implements Runnable {
        SolverTask(P pos, M move, PuzzleNode<P, M> prev) {
            super(pos, move, prev);
        }
        public void run() {
            //如果有一個線程找到了答案,則return,通過ValueLatch中isSet CountDownlatch閉鎖實現;
            //為類避免死鎖,將已經掃描的節點放入set集合中,避免繼續掃描產生死迴圈
            if (solution.isSet() || seen.putIfAbsent(pos, true) != null){
                return; // already solved or seen this position
            }
            if (puzzle.isGoal(pos)) {
                solution.setValue(this);
            } else {
                for (M m : puzzle.legalMoves(pos))
                    exec.execute(newTask(puzzle.move(pos, m), m, this));
            }
        }
    }
}

 

  

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 參考網站:http://blog.csdn.net/u013242177/article/details/50437358 首先要包含commdlg.h頭文件,這個是通用對話框的頭文件,包括文件對話框,顏色對話框,列印對話框等。 然後再聲明一個CHOOSECOLOR類型的變數,聲明一個COLORRE ...
  • 活躍性危險 一、死鎖 發生:每個人都不願意放棄自己的鎖,確想要別人的鎖,這就會導致死鎖 1.鎖順序死鎖:如果每個線程以固定的順序獲取鎖,那麼至少在程式中不會出現鎖順序導致的死鎖; 因為順序固定如:所有線程:A-B-C 則無問題,如果一個A-B B-A則會發生死鎖 例子1:簡單死鎖 例子2:轉賬死鎖 ...
  • 轉自:http://blog.csdn.net/yixianfeng41/article/details/52591585 一、BMP文件由文件頭、點陣圖信息頭、顏色信息和圖形數據四部分組成。 顏色表用於說明點陣圖中的顏色,它有若幹個表項,每一個表項是一個RGBQUAD類型的結構,定義一種顏色。RGBQ ...
  • java存儲: 1)寄存器:這是最快的存儲區,位於處理器的內部。但是寄存器的數量有限,所以寄存器根據需求進行分配。我們不能直接進行操作。 2)堆棧:位於通用RAM中,可以通過堆棧指針從處理器那裡獲取直接支持。堆棧指針往下移動,則分配新的記憶體。網上移動,則釋放記憶體。但是 在創建程式的時候必須知道存儲在 ...
  • w3c 中的定義:鏈接 <a>http://www.w3school.com.cn/jquery/event_change.asp<a> jQuery 事件 - change() 方法 定義和用法 當元素的值發生改變時,會發生 change 事件。 該事件僅適用於文本域(text field),以及 ...
  • Python進階 對象,名字以及綁定 1、一切皆對象 哲學: Python中一切皆對象 1.1 數據模型 對象,值以及類型 對象是 對數據的抽象。 程式中所有的數據都是對象或對象之間的關係表示的。(在某種意義上,為順應馮·諾依曼“存儲式電腦”的模型, 中的代碼也是對象。) 中每一個對象都有一個身份 ...
  • 1 2 3 4 5 ...
  • 1.get與post的區別 Get和Post方法都是對伺服器的請求方式,只是他們傳輸表單的方式不一樣。 下麵我們就以傳輸一個表單的數據為例,來分析get與Post的區別 1.1 get方法 jsp中的代碼form表單代碼 1.2 action包中servlet的doGet方法中的代碼 註意:acti ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...