溫馨提示×

溫馨提示×

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

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

Java線程池的本質(zhì)以及作用是什么

發(fā)布時間:2021-09-18 17:05:07 來源:億速云 閱讀:211 作者:柒染 欄目:編程語言

Java線程池的本質(zhì)以及作用是什么,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

熟悉java多線程的 朋友一定十分了解java的線程池,jdk中的核心實現(xiàn)類為java.util.concurrent.ThreadPoolExecutor。大家可能 了解到它的原理,甚至看過它的源碼;但是就像我一樣,大家可能對它的作用存在誤解。現(xiàn)在問題來了,jdk為什么要提供java線程池?使用java線程池 對于每次都創(chuàng)建一個新Thread有什么優(yōu)勢?

對線程池的誤解

很長一段時間里我一直以為java線程池是為了提高多線程下創(chuàng)建線程的效率。創(chuàng)建好一些線程并緩存在線程池里,后面來了請求(Runnable)就 從連接池中取出一個線程處理請求;這樣就避免了每次創(chuàng)建一個新Thread對象。直到前段時間我看到一篇Neal Gafter(和Joshua  Bloch合著了《Java Puzzlers》,現(xiàn)任職于微軟,主要從事.NET語言方面的工作)的訪談,里面有這么一段談話(http://www.infoq.com/cn/articles/neal-gafter-on-java):

Java線程池的本質(zhì)以及作用是什么

乍一看,大神的思路就是不一樣:java線程池是為了防止java線程占用太多資源?

雖然是java大神的訪談,但是也不能什么都信,你說占資源就占資源?還是得寫測試用例測一下。

首先驗證下我的理解:

java線程池和創(chuàng)建java線程哪個效率高?

直接上測試用例:

public class ThreadPoolTest extends TestCase {     private static final int COUNT = 10000;      public void testThreadPool() throws InterruptedException {         CountDownLatch countDownLatch = new CountDownLatch(COUNT);         ExecutorService executorService = Executors.newFixedThreadPool(100);         long bg = System.currentTimeMillis();         for (int i = 0; i < COUNT; i++) {     Runnable command = new TestRunnable(countDownLatch);     executorService.execute(command);         }         countDownLatch.await();         System.out.println("testThreadPool:" + (System.currentTimeMillis() - bg));     }      public void testNewThread() throws InterruptedException {         CountDownLatch countDownLatch = new CountDownLatch(COUNT);         long bg = System.currentTimeMillis();         for (int i = 0; i < COUNT; i++) {     Runnable command = new TestRunnable(countDownLatch);     Thread thread = new Thread(command);     thread.start();         }         countDownLatch.await();         System.out.println("testNewThread:" + (System.currentTimeMillis() - bg));     }      private static class TestRunnable implements Runnable {         private final CountDownLatch countDownLatch;          TestRunnable(CountDownLatch countDownLatch) {     this.countDownLatch = countDownLatch;         }          @Override         public void run() {     countDownLatch.countDown();         }     } }

這里使用Executors.newFixedThreadPool(100)是為了控制線程池的核心連接數(shù)和***連接數(shù)一樣大,都為100。

我的機子上的測試結(jié)果:

testThreadPool:31
testNewThread:624

可以看到,使用線程池處理10000個請求的處理時間為31ms,而每次啟用新線程的處理時間為624ms。

好了,使用線程池確實要比每次都創(chuàng)建新線程要快一些;但是testNewThread一共耗時624ms,算下平均每次請求的耗時為:

624ms/10000=62.4us

每次創(chuàng)建并啟動線程的時間為62.4微秒。根據(jù)80/20原理,這點兒時間根本可以忽略不計。所以線程池并不是為了效率設(shè)計的。

java線程池是為了節(jié)約資源?

再上測試用例:

public class ThreadPoolTest extends TestCase {     public void testThread() throws InterruptedException {         int i = 1;         while (true) {     Runnable command = new TestRunnable();     Thread thread = new Thread(command);     thread.start();     System.out.println(i++);         }     }      private static class TestRunnable implements Runnable {         @Override         public void run() {     try {         Thread.sleep(1000);     } catch (InterruptedException e) {         e.printStackTrace();     }         }     } }

以上用例模擬每次請求都創(chuàng)建一個新線程處理請求,然后默認每個請求的處理時間為1000ms。而在我的機子上當請求數(shù)達到1096時會內(nèi)存溢出:

java.lang.OutOfMemoryError: unable to create new native thread

為什么會拋OOM Error呢?因為jvm會為每個線程分配一定內(nèi)存(JDK5.0以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256K,也可以通過jvm參數(shù)-Xss來設(shè)置),所以當線程數(shù)達到一定數(shù)量時就報了該error。

設(shè)想如果不使用java線程池,而為每個請求都創(chuàng)建一個新線程來處理該請求,當請求量達到一定數(shù)量時一定會內(nèi)存溢出的;而我們使用java線程池的話,線程數(shù)量一定會<=maximumPoolSize(線程池的***線程數(shù)),所以設(shè)置合理的話就不會造成內(nèi)存溢出。

現(xiàn)在問題明朗了:java線程池是為了防止內(nèi)存溢出,而不是為了加快效率。

淺談java線程池

上文介紹了java線程池啟動太多會造成OOM,使用java線程池也應該設(shè)置合理的線程數(shù)數(shù)量;否則應用可能十分不穩(wěn)定。然而該如何設(shè)置這個數(shù)量呢?我們可以通過這個公式來計算:

(MaxProcessMemory &ndash; JVMMemory &ndash; ReservedOsMemory) / (ThreadStackSize) = Max number of threads

  • MaxProcessMemory     進程***的內(nèi)存

  • JVMMemory                 JVM內(nèi)存

  • ReservedOsMemory     JVM的本地內(nèi)存

  • ThreadStackSize            線程棧的大小

MaxProcessMemory

MaxProcessMemory:進程***的尋址空間,當然也不能超過虛擬內(nèi)存和物理內(nèi)存的總和。關(guān)于不同系統(tǒng)的進程可尋址的***空間,可參考下面表格:

Maximum Address Space Per Process


Operating System

Maximum Address Space Per Process

Redhat Linux 32 bit

2 GB

Redhat Linux 64 bit

3 GB

Windows 98/2000/NT/Me/XP

2 GB

Solaris x86 (32 bit)

4 GB

Solaris 32 bit

4 GB

Solaris 64 bit

Terabytes

JVMMemory

JVMMemory: Heap + PermGen,即堆內(nèi)存和***代內(nèi)存和(注意,不包括本地內(nèi)存)。

ReservedOsMemory

ReservedOSMemory:Native heap,即JNI調(diào)用方法所占用的內(nèi)存。

ThreadStackSize

ThreadStackSize:線程棧的大小,JDK5.0以后每個線程堆棧大小默認為1M,以前每個線程堆棧大小為256K;可以通過jvm參數(shù)-Xss來設(shè)置;注意-Xss是jvm的非標準參數(shù),不強制所有平臺的jvm都支持。

如何調(diào)大線程數(shù)?

如果程序需要大量的線程,現(xiàn)有的設(shè)置不能達到要求,那么可以通過修改MaxProcessMemory,JVMMemory,ThreadStackSize這三個因素,來增加能創(chuàng)建的線程數(shù):

  • MaxProcessMemory 使用64位操作系統(tǒng)

  • JVMMemory   減少JVMMemory的分配

  • ThreadStackSize  減小單個線程的棧大小

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。

向AI問一下細節(jié)

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

AI