中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java并發ThreadPoolExecutor如何使用

發布時間:2023-02-28 16:55:43 來源:億速云 閱讀:101 作者:iii 欄目:開發技術

這篇文章主要介紹“java并發ThreadPoolExecutor如何使用”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“java并發ThreadPoolExecutor如何使用”文章能幫助大家解決問題。

一. 線程池的簡單原理

當一個任務提交到線程池ThreadPoolExecutor時,該任務的執行如下圖所示。

java并發ThreadPoolExecutor如何使用

  • 如果當前運行的線程數小于corePoolSzie(核心線程數),則創建新線程來執行任務(需要獲取全局鎖);

  • 如果當前運行的線程數等于或大于corePoolSzie,則將任務加入BlockingQueue(任務阻塞隊列);

  • 如果BlockingQueue已滿,則創建新的線程來執行任務(需要獲取全局鎖);

  • 如果創建新線程會使當前線程數大于maximumPoolSize(最大線程數),則拒絕任務并調用RejectedExecutionHandlerrejectedExecution() 方法。

由于ThreadPoolExecutor存儲工作線程使用的集合是HashSet,因此執行上述步驟1和步驟3時需要獲取全局鎖來保證線程安全,而獲取全局鎖會導致線程池性能瓶頸,因此通常情況下,線程池完成預熱后(當前線程數大于等于corePoolSize),線程池的execute() 方法都是執行步驟2。

二. 線程池的創建

通過ThreadPoolExecutor能夠創建一個線程池,ThreadPoolExecutor的構造函數簽名如下。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler)
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

通過ThreadPoolExecutor創建線程池時,需要指定線程池的核心線程數最大線程數線程保活時間線程保活時間單位任務阻塞隊列,并按需指定線程工廠飽和拒絕策略,如果不指定線程工廠飽和拒絕策略,則ThreadPoolExecutor會使用默認的線程工廠飽和拒絕策略。下面分別介紹這些參數的含義。

參數含義
corePoolSize核心線程數,即線程池的基本大小。當一個任務被提交到線程池時,如果線程池的線程數小于corePoolSize,那么無論其余線程是否空閑,也需創建一個新線程來執行任務。
maximumPoolSize最大線程數。當線程池中線程數大于等于corePoolSize時,新提交的任務會加入任務阻塞隊列,但是如果任務阻塞隊列已滿且線程數小于maximumPoolSize,此時會繼續創建新的線程來執行任務。該參數規定了線程池允許創建的最大線程數
keepAliveTime線程保活時間。當線程池的線程數大于核心線程數時,多余的空閑線程會最大存活keepAliveTime的時間,如果超過這個時間且空閑線程還沒有獲取到任務來執行,則該空閑線程會被回收掉。
unit線程保活時間單位。通過TimeUnit指定線程保活時間的時間單位,可選單位有DAYS(天),HOURS(時),MINUTES(分),SECONDS(秒),MILLISECONDS(毫秒),MICROSECONDS(微秒)和NANOSECONDS(納秒),但無論指定什么時間單位,ThreadPoolExecutor統一會將其轉換為NANOSECONDS
workQueue任務阻塞隊列。線程池的線程數大于等于corePoolSize時,新提交的任務會添加到workQueue中,所有線程執行完上一個任務后,會循環從workQueue中獲取任務來執行。
threadFactory創建線程的工廠。可以通過線程工廠給每個創建出來的線程設置更有意義的名字。
handler飽和拒絕策略。如果任務阻塞隊列已滿且線程池中的線程數等于maximumPoolSize,說明線程池此時處于飽和狀態,應該執行一種拒絕策略來處理新提交的任務。

三. 線程池執行任務

1. 執行無返回值任務

通過ThreadPoolExecutorexecute() 方法,能執行Runnable任務,示例如下。

public class ThreadPoolExecutorTest {
    @Test
    public void ThreadPoolExecutor執行簡單無返回值任務() throws Exception {
        // 創建一個線程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS, new ArrayBlockingQueue&lt;&gt;(300));
        // 創建兩個任務
        Runnable firstRunnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("第一個任務執行");
            }
        };
        Runnable secondRunnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("第二個任務執行");
            }
        };
        // 讓線程池執行任務
        threadPoolExecutor.execute(firstRunnable);
        threadPoolExecutor.execute(secondRunnable);
        // 讓主線程睡眠1秒,等待線程池中的任務被執行完畢
        Thread.sleep(1000);
    }
}

運行測試程序,結果如下。

java并發ThreadPoolExecutor如何使用

2. 執行有返回值任務

通過ThreadPoolExecutorsubmit() 方法,能夠執行Callable任務,通過submit() 方法返回的RunnableFuture能夠拿到異步執行的結果。示例如下。

public class ThreadPoolExecutorTest {
    @Test
    public void ThreadPoolExecutor執行簡單有返回值任務() throws Exception {
        // 創建一個線程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS, new ArrayBlockingQueue&lt;&gt;(300));
        // 創建兩個任務,任務執行完有返回值
        Callable&lt;String&gt; firstCallable = new Callable&lt;String&gt;() {
            @Override
            public String call() throws Exception {
                return "第一個任務返回值";
            }
        };
        Callable&lt;String&gt; secondCallable = new Callable&lt;String&gt;() {
            @Override
            public String call() throws Exception {
                return "第二個任務返回值";
            }
        };
        // 讓線程池執行任務
        Future&lt;String&gt; firstFuture = threadPoolExecutor.submit(firstCallable);
        Future&lt;String&gt; secondFuture = threadPoolExecutor.submit(secondCallable);
        // 獲取執行結果,拿不到結果會阻塞在get()方法上
        System.out.println(firstFuture.get());
        System.out.println(secondFuture.get());
    }
}

運行測試程序,結果如下。

java并發ThreadPoolExecutor如何使用

3. 執行有返回值任務時拋出錯誤

如果ThreadPoolExecutor在執行Callable任務時,在Callable任務中拋出了異常并且沒有捕獲,那么這個異常是可以通過Futureget() 方法感知到的。示例如下。

public class ThreadPoolExecutorTest {
    @Test
    public void ThreadPoolExecutor執行簡單有返回值任務時拋出錯誤() {
        // 創建一個線程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(300));
        // 創建一個任務,任務有返回值,但是執行過程中拋出異常
        Callable<String> exceptionCallable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                throw new RuntimeException("發生了異常");
            }
        };
        // 讓線程池執行任務
        Future<String> exceptionFuture = threadPoolExecutor.submit(exceptionCallable);
        try {
            System.out.println(exceptionFuture.get());
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

運行測試程序,結果如下。

java并發ThreadPoolExecutor如何使用

4. ThreadPoolExecutor通過submit方式執行Runnable

ThreadPoolExecutor可以通過submit() 方法來運行Runnable任務,并且還可以異步獲取執行結果。示例如下。

public class ThreadPoolExecutorTest {
    @Test
    public void ThreadPoolExecutor通過submit的方式來提交并執行Runnable() throws Exception {
        // 創建一個線程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS, new ArrayBlockingQueue&lt;&gt;(300));
        // 創建結果對象
        MyResult myResult = new MyResult();
        // 創建Runnable對象
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                myResult.setResult("任務執行了");
            }
        };
        // 通過ThreadPoolExecutor的submit()方法提交Runnable
        Future&lt;MyResult&gt; resultFuture = threadPoolExecutor.submit(runnable, myResult);
        // 獲取執行結果
        MyResult finalResult = resultFuture.get();
        // myResult和finalResult的地址實際相同
        Assert.assertEquals(myResult, finalResult);
        // 打印執行結果
        System.out.println(resultFuture.get().getResult());
    }
    private static class MyResult {
        String result;
        public MyResult() {}
        public MyResult(String result) {
            this.result = result;
        }
        public String getResult() {
            return result;
        }
        public void setResult(String result) {
            this.result = result;
        }
    }
}

運行測試程序,結果如下。

java并發ThreadPoolExecutor如何使用

實際上ThreadPoolExecutorsubmit() 方法無論是提交Runnable任務還是Callable任務,都是將任務封裝成了RunnableFuture接口的子類FutureTask,然后調用ThreadPoolExecutorexecute() 方法來執行FutureTask

四. 關閉線程池

關閉線程池可以通過ThreadPoolExecutorshutdown() 方法,但是shutdown() 方法不會去中斷正在執行任務的線程,所以如果線程池里有Worker正在執行一個永遠不會結束的任務,那么shutdown() 方法是無法關閉線程池的。示例如下。

public class ThreadPoolExecutorTest {
    @Test
    public void 通過shutdown關閉線程池() {
        // 創建一個線程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(300));
        // 創建Runnable對象
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                    LockSupport.parkNanos(1000 * 1000 * 1000);
                }
                System.out.println(Thread.currentThread().getName() + " 被中斷");
            }
        };
        // 讓線程池執行任務
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.execute(runnable);
        // 調用shutdown方法關閉線程池
        threadPoolExecutor.shutdown();
        // 等待3秒觀察現象
        LockSupport.parkNanos(1000 * 1000 * 1000 * 3L);
    }
}

運行測試程序,會發現在主線程中等待3秒后,也沒有得到預期的打印結果。如果上述測試程序中使用shutdownNow,則是可以得到預期打印結果的,示例如下。

public class ThreadPoolExecutorTest {
    @Test
    public void 通過shutdownNow關閉線程池() {
        // 創建一個線程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(300));
        // 創建Runnable對象
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                    LockSupport.parkNanos(1000 * 1000 * 1000);
                }
                System.out.println(Thread.currentThread().getName() + " 被中斷");
            }
        };
        // 讓線程池執行任務
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.execute(runnable);
        // 調用shutdown方法關閉線程池
        threadPoolExecutor.shutdownNow();
        // 等待3秒觀察現象
        LockSupport.parkNanos(1000 * 1000 * 1000 * 3L);
    }
}

運行測試程序,打印如下。

java并發ThreadPoolExecutor如何使用

因為測試程序中的任務是響應中斷的,而ThreadPoolExecutorshutdownNow() 方法會中斷所有Worker,所以執行shutdownNow() 方法后,正在運行的任務會響應中斷并結束運行,最終線程池關閉。

假如線程池中運行著一個永遠不會結束的任務,且這個任務不響應中斷,那么無論是shutdown() 方法還是shutdownNow() 方法,都是無法關閉線程池的。

關于“java并發ThreadPoolExecutor如何使用”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

当涂县| 新化县| 佛冈县| 广昌县| 萍乡市| 临漳县| 眉山市| 苏尼特右旗| 克什克腾旗| 东乡族自治县| 固安县| 怀集县| 涿州市| 咸宁市| 称多县| 凉山| 根河市| 桐柏县| 武冈市| 拉萨市| 天水市| 苏尼特左旗| 上饶市| 黄平县| 仁布县| 桃源县| 永泰县| 行唐县| 泊头市| 镇康县| 新沂市| 浦江县| 清涧县| 高密市| 霍林郭勒市| 天镇县| 招远市| 永川市| 遂昌县| 施秉县| 财经|