欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

線程池中execute與submit的區(qū)別說明

 更新時間:2022年03月23日 14:54:20   作者:每年進步一點點  
這篇文章主要介紹了線程池execute與submit的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

線程池execute與submit區(qū)別

在使用線程池的時候,看到execute()與submit()方法。都可以使用線程池執(zhí)行一個任務,但是兩者有什么區(qū)別呢?

execute

void execute(Runnable command);

submit

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);

相同點:

1 都可以執(zhí)行任務

2 參數(shù)都支持runnable

不同點:

1 submit支持接收返回值 詳見例1。

2 execute 任務里面的異常必須捕獲,不能向上拋出;submit支持的Callable支持向上拋出異常,需要由返回值.get()來進行接收。詳見例2。

例1

public class ExecutorTest {?
? ? public static ExecutorService executorService = Executors.newFixedThreadPool(3);?
? ? public static void main(String[] args) {
? ? ? ? Future<?> result1 = executorService.submit(new Callable() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public Object call() throws Exception {
? ? ? ? ? ? ? ? TimeUnit.SECONDS.sleep(10);
? ? ? ? ? ? ? ? System.out.println("Thread1 sleep 10 seconds");
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? Future<?> result2 = executorService.submit(new Callable() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public Object call() throws Exception {
? ? ? ? ? ? ? ? TimeUnit.SECONDS.sleep(5);
? ? ? ? ? ? ? ? System.out.println("Thread2 sleep 5 seconds");
? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? try {
? ? ? ? ? ? System.out.println("Thread1 return:"+result1.get());
? ? ? ? ? ? System.out.println("Thread2 return:"+result2.get());
? ? ? ? ? ? System.out.println("finished");
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (ExecutionException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }?
? ? }
}

該例子的返回結果:

Thread2 sleep 5 seconds
Thread1 sleep 10 seconds
Thread1 return:true
Thread2 return:false
finished

解釋:可以看到接收到了兩個線程的返回結果。利用thread.sleep來模擬耗時操作,直到兩個線程執(zhí)行完畢之后,才會輸出finished。利用Callable的返回阻塞,來等待這n個線程的執(zhí)行完畢,然后將這n個線程的結果響應回去;其執(zhí)行時間,基本上取決于最耗時的那個線程。

適用場景:在某些情況下,需要獲得一組線程的結果,給調用端。

例2

而execute不支持向上拋出異常,必須將異常捕獲。

線程池submit和execute方法原理

線程池的作用

1. 避免大量的線程強占資源

2. 避免大量的線程創(chuàng)建和銷毀帶來的開銷  

線程池的原理

創(chuàng)建線程池的時候,可以使用executors的靜態(tài)方法,也可以使用new ThreadPoolExecutor的方式手動創(chuàng)建線程池,通過在線程池中指定參數(shù)達到創(chuàng)建不同類型的線程池的效果

其中,executors底層其實也是調用的new ThreadPoolExecutor()的方式創(chuàng)建的,是對不同線程池的封裝,

線程的執(zhí)行有兩種方式,一種是submit(runnable v)的形式,一種是execute(runnable b) 的形式,不同的是submit可以返回一個future的實現(xiàn)類,相同的一點是submit底層其實也是調用的execute

調用execut方法,首先判斷傳入的參數(shù)是否為空,如果為空,拋出異常,如果不為空,使用獲取ctl值,計算出當前線程狀態(tài)碼,通過狀態(tài)碼計算出當前線程池工作線程是否小于核心線程數(shù)量

如果小于,判斷添加工作線程操作是否正常,如果正常,直接返回,如果不正常,繼續(xù)執(zhí)行獲取ctl值,在添加工作線程的過程中,首先通過循環(huán)的方式保證ctl在加1的情況下狀態(tài)同步,如果不同步,一直循環(huán)到同步為止,添加完成后,創(chuàng)建線程工作對象,把工作線程添加到set集合中,并執(zhí)行.start,如果執(zhí)行不成功,從set中刪除添加的worker對象,并且ctl回滾到之前沒有自增的值.

如果上述中添加工作線程失敗,或者當前線程池中工作線程數(shù)量操作和信息數(shù)量,執(zhí)行下列邏輯

判斷當前線程池狀態(tài)是否是running狀態(tài):

如果不是running狀態(tài),或者是running狀態(tài),并且添加到線程隊列失敗,重新添加個工作線程,此時入?yún)⒅械诙€參數(shù)用于添加工作線程的邏輯中當前工作線程數(shù)量與最大線程數(shù)量做對比,如果添加失敗,執(zhí)行reject處理類處理

如果是running狀態(tài),并且添加隊列成功,重新獲取ctl值,判斷當前線程池狀態(tài)如果是不是running狀態(tài),并且從對象中刪除成功,則當前線程交給拒絕線程處理器處理,如果不滿足上面條件,判斷當前線程池的工作線程數(shù)如果為0,重新添加一個不帶任務的線程. 

//AbstractExecutorService.java文件
? ? // executorService 中的 submit 方法
? ? public Future<?> submit(Runnable task) {
? ? ? ? // 首先判斷傳入的runnable 對象是否為空
? ? ? ? if (task == null) throw new NullPointerException();
? ? ? ? // 創(chuàng)建一個 futuretask 對象
? ? ? ? RunnableFuture<Void> ftask = newTaskFor(task, null);
? ? ? ? execute(ftask);
? ? ? ? return ftask;
? ? }
? ? // 根據(jù)runnable 創(chuàng)建一個futuretask對象
? ? protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
? ? ? ? return new FutureTask<T>(runnable, value);
? ? }
? ? // ThreadPoolExecutor.java文件
? ? // 執(zhí)行創(chuàng)建線程池的方法
? ? public void execute(Runnable command) {
? ? ? ? // 首先判斷傳入的線程是否為空
? ? ? ? if (command == null)
? ? ? ? ? ? // 為空,拋出異常
? ? ? ? ? ? throw new NullPointerException();
? ? ? ? // 獲取線程池的狀態(tài)碼, 這個狀態(tài)碼是自增的,原子類型的自增, 在執(zhí)行addworker后ctl會加1
? ? ? ? int c = ctl.get();
? ? ? ? // 通過狀態(tài)碼,獲取線程池中的線程的數(shù)量,如果小于核心數(shù)量
? ? ? ? if (workerCountOf(c) < corePoolSize) {
? ? ? ? ? ? // 添加線程到線程池,并且為true時使用核心線程數(shù)作為邊界,如果false ,使用最大數(shù)量線程數(shù)作為邊界
? ? ? ? ? ? if (addWorker(command, true))
? ? ? ? ? ? ? ? // 添加完成后,返回
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? // 如果添加失敗,重新獲取狀態(tài)值
? ? ? ? ? ? c = ctl.get();
? ? ? ? }
? ? ? ? // 執(zhí)行下面邏輯有兩種情況
? ? ? ? // ? ? ?1. 工作線程數(shù)大于核心線程
? ? ? ? // ? ? ?2. 添加線程時出錯
? ? ? ? // 如果線程池中線程的數(shù)量大于核心的數(shù)量, 判斷如果是運行狀態(tài), 并且也把線程加進了阻塞隊列 workQueue 中
? ? ? ? if (isRunning(c) && workQueue.offer(command)) {
? ? ? ? ? ? // 重新獲取 線程池 狀態(tài)值
? ? ? ? ? ? int recheck = ctl.get();
? ? ? ? ? ? // 判斷當前線程池如果不是運行狀態(tài),并且成功從隊列中移除(從workQueue中移除線程, 并嘗試終止線程池)
? ? ? ? ? ? if (! isRunning(recheck) && remove(command))
? ? ? ? ? ? ? ? // 執(zhí)行拒絕執(zhí)行線程的處理
? ? ? ? ? ? ? ? reject(command);
? ? ? ? ? ? ? ? // 如果工作線程數(shù)為0
? ? ? ? ? ? else if (workerCountOf(recheck) == 0)
? ? ? ? ? ? ? ? // 添加一個null的工作包裝對象
? ? ? ? ? ? ? ? addWorker(null, false);
? ? ? ? } else if (!addWorker(command, false))
? ? ? ? ? ? // 如果添加到線程池中出錯,執(zhí)行拒接的線程
? ? ? ? ? ? reject(command);
? ? }
? ? // 創(chuàng)建一個原子類對象用于計算線程的中狀態(tài)
? ? private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
? ? // integer.size 為 32
? ? private static final int COUNT_BITS = Integer.SIZE - 3;
? ? private static final int CAPACITY ? = (1 << COUNT_BITS) - 1;
? ? // 即高3位為1,低29位為0,該狀態(tài)的線程池會接收新任務,也會處理在阻塞隊列中等待處理的任務
? ? private static final int RUNNING ? ?= -1 << COUNT_BITS;
? ? // 即高3位為0,低29位為0,該狀態(tài)的線程池不會再接收新任務,但還會處理已經提交到阻塞隊列中等待處理的任務
? ? private static final int SHUTDOWN ? = ?0 << COUNT_BITS;
? ? // 即高3位為001,低29位為0,該狀態(tài)的線程池不會再接收新任務,不會處理在阻塞隊列中等待的任務,而且還會中斷正在運行的任務
? ? private static final int STOP ? ? ? = ?1 << COUNT_BITS;
? ? // 即高3位為010,低29位為0,所有任務都被終止了,workerCount為0,為此狀態(tài)時還將調用terminated()方法
? ? private static final int TIDYING ? ?= ?2 << COUNT_BITS;
? ? // 即高3位為100,低29位為0,terminated()方法調用完成后變成此狀態(tài) ?
? ? private static final int TERMINATED = ?3 << COUNT_BITS;
? ? // 用戶計算線程的狀態(tài) 32位中 高3位為1 低29位為0?
? ? private static int runStateOf(int c) ? ? { return c & ~CAPACITY; }
? ? // 用于計算線程池中線程的數(shù)量 32位中 高3位為0 ?低29位為1
? ? private static int workerCountOf(int c) ?{ return c & CAPACITY; }
? ? // rs 為 runState, wc 為 workerCount 通過工作狀態(tài)和線程數(shù)量來計算出 ctl
? ? private static int ctlOf(int rs, int wc) { return rs | wc; }
? ? // 添加工作線程的方法
? ? private boolean addWorker(Runnable firstTask, boolean core) {
? ? ? ? // 設置循環(huán)跳出點,如果執(zhí)行到某個位置,使用break,直接跳出的是這個標簽范圍內的所有循環(huán)
? ? ? ? retry:
? ? ? ? for (;;) {
? ? ? ? ? ? // 獲取線程狀態(tài)
? ? ? ? ? ? int c = ctl.get();
? ? ? ? ? ? int rs = runStateOf(c);
? ? ? ? ? ? // 判斷線程池狀態(tài)是否在shutdown上以及 狀態(tài)不是關閉并且添加的線程不為空,并且線程隊列中的線程不是空的
? ? ? ? ? ? if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
? ? ? ? ? ? ? ? // 如果滿足上面條件,說明線程池已經不適合添加新的線程了, 直接返回false
? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? // 如果不滿足上面條件,說明線程池可以添加線程, 下面這個循環(huán)主要是對ctl進行操作,保證在增1后線程狀態(tài)保持同步
? ? ? ? ? ? for (;;) {
? ? ? ? ? ? ? ? // 獲取工作線程數(shù)量
? ? ? ? ? ? ? ? int wc = workerCountOf(c);
? ? ? ? ? ? ? ? // 判斷當前線程池中工作線程數(shù)量是否大于線程容量,大于核心線程數(shù)或最大線程數(shù)
? ? ? ? ? ? ? ? if (wc >= CAPACITY ||
? ? ? ? ? ? ? ? ? ? wc >= (core ? corePoolSize : maximumPoolSize))
? ? ? ? ? ? ? ? ? ? // 滿足條件,說明當前線程不是適合添加新的線程的
? ? ? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? ? ? // 如果工作數(shù)量少于最大量或者核心線程數(shù)或最大線程數(shù), 工作線程數(shù)加1,即操作ctl,通過cas的方式
? ? ? ? ? ? ? ? if (compareAndIncrementWorkerCount(c))
? ? ? ? ? ? ? ? ? ? // 如果添加成功,跳出內循環(huán),
? ? ? ? ? ? ? ? ? ? break retry;
? ? ? ? ? ? ? ? // 如果添加失敗,重新獲取ctl
? ? ? ? ? ? ? ? c = ctl.get(); ?// Re-read ctl
? ? ? ? ? ? ? ? // 判斷此時線程池狀態(tài)是否已經改變
? ? ? ? ? ? ? ? if (runStateOf(c) != rs)
? ? ? ? ? ? ? ? ? ? //如果狀態(tài)不一致,跳過,重新循環(huán)
? ? ? ? ? ? ? ? ? ? continue retry;
? ? ? ? ? ? ? ? // else CAS failed due to workerCount change; retry inner loop
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? boolean workerStarted = false;
? ? ? ? boolean workerAdded = false;
? ? ? ? // 創(chuàng)建一個線程包裝對象,用于包裝線程
? ? ? ? Worker w = null;
? ? ? ? try {
? ? ? ? ? ? w = new Worker(firstTask);
? ? ? ? ? ? // 創(chuàng)建一個worker 工作線程
? ? ? ? ? ? final Thread t = w.thread;
? ? ? ? ? ? // 判斷創(chuàng)建的線程是否為空
? ? ? ? ? ? if (t != null) {
? ? ? ? ? ? ? ? ?// 如果不為空,獲取鎖對象
? ? ? ? ? ? ? ? final ReentrantLock mainLock = this.mainLock;
? ? ? ? ? ? ? ? // 開始加鎖
? ? ? ? ? ? ? ? mainLock.lock();
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? // 獲取線程池狀態(tài)
? ? ? ? ? ? ? ? ? ? int rs = runStateOf(ctl.get());
? ? ? ? ? ? ? ? ? ? // 如果線程池狀態(tài)是running或者線程池狀態(tài)關閉并且傳入的線程是空的
? ? ? ? ? ? ? ? ? ? if (rs < SHUTDOWN ||
? ? ? ? ? ? ? ? ? ? ? ? (rs == SHUTDOWN && firstTask == null)) {
? ? ? ? ? ? ? ? ? ? ? ? // 判斷創(chuàng)建的工作線程是否是活動狀態(tài)(已經開始還沒有死掉)
? ? ? ? ? ? ? ? ? ? ? ? if (t.isAlive()) // precheck that t is startable
? ? ? ? ? ? ? ? ? ? ? ? ? ? // 如果是活動狀態(tài),拋出 非法線程狀態(tài)異常?
? ? ? ? ? ? ? ? ? ? ? ? ? ? throw new IllegalThreadStateException();
? ? ? ? ? ? ? ? ? ? ? ? // 如果不是活動狀態(tài), 添加到set集合中,這個set集合只有持有mainlock才可以訪問
? ? ? ? ? ? ? ? ? ? ? ? workers.add(w);
? ? ? ? ? ? ? ? ? ? ? ? // 獲取集合長度
? ? ? ? ? ? ? ? ? ? ? ? int s = workers.size();
? ? ? ? ? ? ? ? ? ? ? ? // 如果存放剛才創(chuàng)建的workers工作線程的集合中的線程數(shù)超過最大的池的大小
? ? ? ? ? ? ? ? ? ? ? ? if (s > largestPoolSize)
? ? ? ? ? ? ? ? ? ? ? ? ? ? // 把set集合中的數(shù)量代替原線程池最大值
? ? ? ? ? ? ? ? ? ? ? ? ? ? largestPoolSize = s;
? ? ? ? ? ? ? ? ? ? ? ? workerAdded = true;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } finally {
? ? ? ? ? ? ? ? ? ? // 釋放鎖
? ? ? ? ? ? ? ? ? ? mainLock.unlock();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // 根據(jù)前面的判斷是否需要開啟線程,如果線程已經是活動的,不需要開啟,如果不是活動線程,開啟線程
? ? ? ? ? ? ? ? if (workerAdded) {
? ? ? ? ? ? ? ? ? ? t.start();
? ? ? ? ? ? ? ? ? ? // 開啟成功,設置workerStarted 為 true
? ? ? ? ? ? ? ? ? ? workerStarted = true;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? } finally {
? ? ? ? ? ? // 如果工作線程開啟失敗,調用添加到失敗的線程中
? ? ? ? ? ? if (! workerStarted)
? ? ? ? ? ? ? ? // 從set中移除失敗的線程,并且ctl減1, 并且嘗試終止線程池
? ? ? ? ? ? ? ? addWorkerFailed(w);
? ? ? ? }
? ? ? ? return workerStarted;
? ? }
? ? // 線程開啟失敗后的方法
? ? private void addWorkerFailed(Worker w) {
? ? ? ? // 獲取鎖
? ? ? ? final ReentrantLock mainLock = this.mainLock;
? ? ? ? mainLock.lock();
? ? ? ? try {
? ? ? ? ? ? if (w != null)
? ? ? ? ? ? ? ? // 如果線程不為空,從set集合中移除沒有開啟成功的線程
? ? ? ? ? ? ? ? workers.remove(w);
? ? ? ? ? ? // 減去之前ctl增加的1
? ? ? ? ? ? decrementWorkerCount();
? ? ? ? ? ? // 嘗試中斷線程
? ? ? ? ? ? tryTerminate();
? ? ? ? } finally {
? ? ? ? ? ? mainLock.unlock();
? ? ? ? }
? ? }
? ? // 通過cas方式ctl加1
? ? private boolean compareAndIncrementWorkerCount(int expect) {
? ? ? ? return ctl.compareAndSet(expect, expect + 1);
? ? }
? ? // 移除線程
? ? public boolean remove(Runnable task) {
? ? ? ? // 從等待隊列中一尺線程
? ? ? ? boolean removed = workQueue.remove(task);
? ? ? ? // 嘗試終止線程池
? ? ? ? tryTerminate(); // In case SHUTDOWN and now empty
? ? ? ? return removed;
? ? }
? ? // 使用拒絕處理對象執(zhí)行拒接指定線程
? ? final void reject(Runnable command) {
? ? ? ? handler.rejectedExecution(command, this);
? ? }

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • Druid關閉監(jiān)控頁面關閉不了的問題及解決

    Druid關閉監(jiān)控頁面關閉不了的問題及解決

    這篇文章主要介紹了Druid關閉監(jiān)控頁面關閉不了的問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • java的Console類的使用方法及實例

    java的Console類的使用方法及實例

    這篇文章主要介紹了java的Console類的使用方法及實例的相關資料,需要的朋友可以參考下
    2017-07-07
  • 詳解Springboot如何通過注解實現(xiàn)接口防刷

    詳解Springboot如何通過注解實現(xiàn)接口防刷

    本文主要為大家介紹一種極簡潔、靈活通用接口防刷實現(xiàn)方式、通過在需要防刷的方法加上@Prevent?注解即可實現(xiàn)短信防刷,感興趣的可以了解一下
    2022-09-09
  • IDEA設置JVM可分配內存大小和其他參數(shù)的教程

    IDEA設置JVM可分配內存大小和其他參數(shù)的教程

    這篇文章主要介紹了IDEA設置JVM可分配內存大小和其他參數(shù)的教程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • SpringBoot整合MongoDB的示例

    SpringBoot整合MongoDB的示例

    這篇文章主要介紹了SpringBoot整合MongoDB的示例,幫助大家更好的理解和學習springboot框架,感興趣的朋友可以了解下
    2020-10-10
  • springboot引用kettle實現(xiàn)對接oracle數(shù)據(jù)的示例代碼

    springboot引用kettle實現(xiàn)對接oracle數(shù)據(jù)的示例代碼

    這篇文章主要介紹了springboot引用kettle實現(xiàn)對接oracle數(shù)據(jù),其實kettle集成到springboot里面沒有多少代碼,這個功能最主要的還是ktr文件的編寫,只要ktr編寫好了,放到指定文件夾下,寫個定時任務就完事了,需要的朋友可以參考下
    2022-12-12
  • Java中實現(xiàn)兩個線程交替運行的方法

    Java中實現(xiàn)兩個線程交替運行的方法

    這篇文章主要介紹了Java中實現(xiàn)兩個線程交替運行的方法,本文將給大家分享操作流程,通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2021-12-12
  • mybatis輸出SQL格式化方式

    mybatis輸出SQL格式化方式

    這篇文章主要介紹了mybatis輸出SQL格式化方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • spring boot定時任務接收郵件并且存儲附件的方法講解

    spring boot定時任務接收郵件并且存儲附件的方法講解

    今天小編就為大家分享一篇關于spring boot定時任務接收郵件并且存儲附件的方法講解,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • Java Netty實現(xiàn)心跳機制過程解析

    Java Netty實現(xiàn)心跳機制過程解析

    這篇文章主要介紹了Java Netty實現(xiàn)心跳機制過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-03-03

最新評論