一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java線程池使用與原理詳解

Java線程池使用與原理詳解

2021-01-15 14:00_Yasin Java教程

這篇文章主要為大家詳細介紹了Java線程池使用與原理的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下

線程池是什么?

我們可以利用java很容易創建一個新線程,同時操作系統創建一個線程也是一筆不小的開銷。所以基于線程的復用,就提出了線程池的概念,我們使用線程池創建出若干個線程,執行完一個任務后,該線程會存在一段時間(用戶可以設定空閑線程的存活時間,后面會介紹),等到新任務來的時候就直接復用這個空閑線程,這樣就省去了創建、銷毀線程損耗。當然空閑線程也會是一種資源的浪費(所有才有空閑線程存活時間的限制),但總比頻繁的創建銷毀線程好太多。
下面是我的測試代碼

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/*
 * @TODO 線程池測試
 */
@Test
public void threadPool(){
 
  /*java提供的統計線程運行數,一開始設置其值為50000,每一個線程任務執行完
   * 調用CountDownLatch#coutDown()方法(其實就是自減1)
   * 當所有的線程都執行完其值就為0
  */
  CountDownLatch count = new CountDownLatch(50000);
  long start = System.currentTimeMillis();
  Executor pool = Executors.newFixedThreadPool(10);//開啟線程池最多會創建10個線程
  for(int i=0;i<50000;i++){
    pool.execute(new Runnable() {
      @Override
      public void run() {
        System.out.println("hello");
        count.countDown();
      }
    });
  }
 
  while(count.getCount()!=0){//堵塞等待5w個線程運行完畢
 
  }
  long end = System.currentTimeMillis();
  System.out.println("50個線程都執行完了,共用時:"+(end-start)+"ms");
}
 
 
/**
 *@TODO 手動創建線程測試
 */
@Test
public void thread(){
  CountDownLatch count = new CountDownLatch(50000);
  long start = System.currentTimeMillis();
  for(int i=0;i<50000;i++){
    Thread thread = new Thread(new Runnable() {
 
      @Override
      public void run() {
        System.out.println("hello");
        count.countDown();
      }
    });
    thread.start();
  }
 
  while(count.getCount()!=0){//堵塞等待5w個線程運行完畢
 
  }
  long end = System.currentTimeMillis();
  System.out.println("50000個線程都執行完了,共用時:"+(end-start)+"ms");
 
 
}

使用線程池5w線程運行完大約為400ms,不使用線程池運行大約為4350ms左右,其效率可見一斑(讀者可以自行測試,不過由于電腦配置不一樣,跑出來的數據會有差別,但使用線程池絕對是比創建線程要快的)。

java如何使用線程池?

上面的測試代碼中已經使用了線程池,下面正式介紹一下。

java所有的線程池最頂層是一個Executor接口,其只有一個execute方法,用于執行所有的任務,java又提供了ExecutorService接口繼承自Executor并且擴充了一下方法,在往下就是AbstractExecutorService這個抽象類,其實現了ExecutorService,最后就是ThreadPoolExecutor其繼承自上面的抽象類,我們常使用的java線程池就是創建的這個類的實例。

而上面我們使用Executors是一個工具類,它就是一個語法糖,為我們把各種不同的業務的線程池參數進行封裝,進行new操作。

?
1
2
3
4
5
public static ExecutorService newFixedThreadPool(int nThreads) {
   return new ThreadPoolExecutor(nThreads, nThreads,
                  0L, TimeUnit.MILLISECONDS,
                  new LinkedBlockingQueue<Runnable>());
 }

上面就是Executors.newFixedThreadPool(10)的源碼。

下面重點來了,說一說ThreadPoolExecutor構造方法各參數的意思。

?
1
2
3
4
5
6
7
public ThreadPoolExecutor(int corePoolSize,
              int maximumPoolSize,
              long keepAliveTime,
              TimeUnit unit,
              BlockingQueue<Runnable> workQueue,
              ThreadFactory threadFactory,
              RejectedExecutionHandler handler)

上面這個構造方法是最全的。

下面我們根據源碼來解釋部分參數意思,這樣更有說服力。

下面是ThreadPoolExecutor#execute方法,就是我們上面接口調用的execute實際執行者。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void execute(Runnable command) {
   if (command == null)
     throw new NullPointerException();
 
   int c = ctl.get();
   if (workerCountOf(c) < corePoolSize) {
     if (addWorker(command, true))
       return;
     c = ctl.get();
   }
   if (isRunning(c) && workQueue.offer(command)) {
     int recheck = ctl.get();
     if (! isRunning(recheck) && remove(command))
       reject(command);
     else if (workerCountOf(recheck) == 0)
       addWorker(null, false);
   }
   else if (!addWorker(command, false))
     reject(command);
 }

ctl是一個AtomicInteger實例,是一個提供了原子語句的CAS操作的類,它用來記錄線程池中當前運行的線程數量加上-2^29,workCountOf方法就取得其絕對值(可以去看源碼如何實現),當其小于corePoolSize時,會調用addWorker方法(是用來創建一個新Workder,Workder會創建一個Thread,所以就是創建線程的方法),addWorkd創建線程過程中會跟corePoolSize或者maxnumPoolSize的值比較(當傳入true會根corePoolSize比較,false會根據maxnumPoolSize比較,大于等于其值會創建失敗)。可見如何當前運行中的線程數量小于corePoolSize就是創建并且也會創建成功(
只簡單的討論線程池Running狀態下)。

如果當運行中線程數大于等于corePoolSize時,進入第二個if,isRunning是跟SHUTDOWN(其值=0)比較,之前說過c等于當前運行的線程數量加上-2^29,如果當前當前運行的線程數據達到2^29時其值就=0,isRunning返回false,else中在執行addWorkd也會返回false(addWorkd也對其進行了檢驗),所以這表示線程池最多能支持2^29個線程同時運行(足夠用了)。

workQueue.offer(command)就是將runnable加入等待隊列,加入等待隊列后runWorker方法會從隊列中獲取任務執行的。如果當前隊列采用的是有界隊列(ArrayBlockingQueue)當隊列滿了offer就會返回false,這是就進入else if,看!這里傳入了false,說明這里要跟maxnumPoolSize比較了,如果這里運行的線程數大于等于maxnumPoolSize,那么這個線程任務就要被線程池拒絕了,執行reject(command),拒絕方法中使用了我們ThreadPoolExecutor構造方法中的RejectedExecutionHandler(拒絕策略),后面再詳細解釋。

經過上面的結合源碼的介紹,下面對們ThreadPoolExecutor的參數介紹就好理解了。

線程池中線程創建和拒絕策略

corePoolSize,maxnumPoolSize,BlockingQueue這三個要一塊說

當線程池運行的線程小于corePoolSize時,來一個新線程任務總是會新建一個線程來執行;當大于corePoolSize就會把任務加入到等待隊列blockingQueue中,如果你傳入的BlockingQueue是一個無界隊列(LinkedBlockingQueue)這是隊列可以存放“無窮多”的任務,所有總是會加入隊列成功,跟maxnumPoolSize就沒關系了,這也表示線程池中線程數最多為corePoolSize個;但是如果你傳入的是有界隊列(ArrayBlockingQueue,SynchronousQueue),當隊列滿時,并且線程數小于maxmunPoolSize就是創建新的線程直至線程數大于maxnumPoolSize;如果當線程數量大于maxnumPoolSize時,在加入任務就會被線程池拒絕。

RejectedExecutionHandler拒絕策略java給實現了4個AbortPolicy,CallerRunsPolicy,DiscardOldestPolicy,DiscardPolicy用戶也可以自己實現該接口實現自己的拒絕策略;第一個就是直接拋出異常,我們可以進行trycatch處理;第二個就是該新任務直接運行;第三個是取消隊列中最老的;第四個是取消當前任務。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://blog.csdn.net/u013592964/article/details/78082243

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 九色PORNY蝌蚪视频首页 | 四虎在线网址 | 国语视频高清在线观看 | 国产一卡二卡3卡4卡更新 | 欧美一级高清片 | 免费亚洲成人 | 国产精品久久久久久久福利院 | 满溢游泳池免费土豪全集下拉版 | 韩国日本在线观看 | x8x8在线永久免费观看 | 无遮无挡免费视频 | 天堂成人影院 | 亚洲精品福利一区二区在线观看 | 日韩精品在线视频观看 | 插入肥臀 | 出水小说 | 精品福利视频一区二区三区 | 国产在线成人精品 | 99久久精品国产免费 | 九九影院午夜理论片无码 | 日本加勒比一区 | 国产黄频在线观看高清免费 | 九九久久国产精品免费热6 九九精品视频一区二区三区 | 2022天天干| 特黄a大片免费视频 | 国产高清一区二区三区免费视频 | 国产欧美曰韩一区二区三区 | 九九九九九九 | 污到湿的爽文免费阅读 | 国产高清视频一区二区 | 美女张开双腿让男人捅 | 国内精品视频免费观看 | 日韩毛片在线 | 色姑娘导航 | 成人久久18免费网站 | 日本破处 | 手机在线观看网站免费视频 | 明星h文集合短篇小说 | 国产aaa伦理片 | 亚洲国产高清视频 | 国产亚洲精品精品国产亚洲综合 |