日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

转:JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue

發(fā)布時間:2024/4/17 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 转:JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

從Java5開始,Java提供了自己的線程池。每次只執(zhí)行指定數(shù)量的線程,java.util.concurrent.ThreadPoolExecutor 就是這樣的線程池。以下是我的學習過程。

首先是構造函數(shù)簽名如下:

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

參數(shù)介紹:

corePoolSize?核心線程數(shù),指保留的線程池大小(不超過maximumPoolSize值時,線程池中最多有corePoolSize 個線程工作)。?
maximumPoolSize?指的是線程池的最大大小(線程池中最大有corePoolSize 個線程可運行)。?
keepAliveTime?指的是空閑線程結(jié)束的超時時間(當一個線程不工作時,過keepAliveTime 長時間將停止該線程)。?
unit?是一個枚舉,表示 keepAliveTime 的單位(有NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS,7個可選值)。?
workQueue?表示存放任務的隊列(存放需要被線程池執(zhí)行的線程隊列)。?
handler?拒絕策略(添加任務失敗后如何處理該任務).

1、線程池剛創(chuàng)建時,里面沒有一個線程。任務隊列是作為參數(shù)傳進來的。不過,就算隊列里面有任務,線程池也不會馬上執(zhí)行它們。
2、當調(diào)用 execute() 方法添加一個任務時,線程池會做如下判斷:
? ? a. 如果正在運行的線程數(shù)量小于 corePoolSize,那么馬上創(chuàng)建線程運行這個任務;
? ? b. 如果正在運行的線程數(shù)量大于或等于 corePoolSize,那么將這個任務放入隊列。
? ? c. 如果這時候隊列滿了,而且正在運行的線程數(shù)量小于 maximumPoolSize,那么還是要創(chuàng)建線程運行這個任務;
? ? d. 如果隊列滿了,而且正在運行的線程數(shù)量大于或等于 maximumPoolSize,那么線程池會拋出異常,告訴調(diào)用者“我不能再接受任務了”。
3、當一個線程完成任務時,它會從隊列中取下一個任務來執(zhí)行。
4、當一個線程無事可做,超過一定的時間(keepAliveTime)時,線程池會判斷,如果當前運行的線程數(shù)大于 corePoolSize,那么這個線程就被停掉。所以線程池的所有任務完成后,它最終會收縮到 corePoolSize 的大小。
? ? ? ?這個過程說明,并不是先加入任務就一定會先執(zhí)行。假設隊列大小為 4,corePoolSize為2,maximumPoolSize為6,那么當加入15個任務時,執(zhí)行的順序類似這樣:首先執(zhí)行任務 1、2,然后任務3~6被放入隊列。這時候隊列滿了,任務7、8、9、10 會被馬上執(zhí)行,而任務 11~15 則會拋出異常。最終順序是:1、2、7、8、9、10、3、4、5、6。當然這個過程是針對指定大小的ArrayBlockingQueue<Runnable>來說,如果是LinkedBlockingQueue<Runnable>,因為該隊列無大小限制,所以不存在上述問題。

示例一,LinkedBlockingQueue<Runnable>隊列使用:

import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Created on 2011-12-28 * <p>Description: [Java 線程池學習]</p> * @author shixing_11@sina.com */ public class ThreadPoolTest implements Runnable { public void run() { synchronized(this) { try{ System.out.println(Thread.currentThread().getName()); Thread.sleep(3000); }catch (InterruptedException e){ e.printStackTrace(); } } } public static void main(String[] args) { BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(); ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 6, 1, TimeUnit.DAYS, queue); for (int i = 0; i < 10; i++) { executor.execute(new Thread(new ThreadPoolTest(), "TestThread".concat(""+i))); int threadSize = queue.size(); System.out.println("線程隊列大小為-->"+threadSize); } executor.shutdown(); } }

輸出結(jié)果如下:

?

線程隊列大小為-->0
線程名稱:pool-1-thread-1
線程隊列大小為-->0
線程隊列大小為-->1
線程隊列大小為-->2
線程隊列大小為-->3
線程隊列大小為-->4
線程隊列大小為-->5
線程隊列大小為-->6
線程隊列大小為-->7
線程隊列大小為-->8
線程名稱:pool-1-thread-2
線程名稱:pool-1-thread-1
線程名稱:pool-1-thread-2
線程名稱:pool-1-thread-1
線程名稱:pool-1-thread-2
線程名稱:pool-1-thread-1
線程名稱:pool-1-thread-2
線程名稱:pool-1-thread-1
線程名稱:pool-1-thread-2

可見,線程隊列最大為8,共執(zhí)行了10個線線程。因為是從線程池里運行的線程,所以雖然將線程的名稱設為"TestThread".concat(""+i),但輸出后還是變成了pool-1-thread-x。

示例二,LinkedBlockingQueue<Runnable>隊列使用:

import java.util.concurrent.BlockingQueue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Created on 2011-12-28 * <p>Description: [Java 線程池學習]</p> * @author shixing_11@sina.com */ public class ThreadPoolTest implements Runnable { public void run() { synchronized(this) { try{ System.out.println("線程名稱:"+Thread.currentThread().getName()); Thread.sleep(3000); //休眠是為了讓該線程不至于執(zhí)行完畢后從線程池里釋放 }catch (InterruptedException e){ e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(4); //固定為4的線程隊列 ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 6, 1, TimeUnit.DAYS, queue); for (int i = 0; i < 10; i++) { executor.execute(new Thread(new ThreadPoolTest(), "TestThread".concat(""+i))); int threadSize = queue.size(); System.out.println("線程隊列大小為-->"+threadSize); } executor.shutdown(); } }

輸出結(jié)果如下:

?

線程隊列大小為-->0
線程隊列大小為-->0
線程隊列大小為-->1
線程隊列大小為-->2
線程隊列大小為-->3
線程隊列大小為-->4
線程隊列大小為-->4
線程隊列大小為-->4
線程隊列大小為-->4
線程名稱:pool-1-thread-1
線程名稱:pool-1-thread-3
線程名稱:pool-1-thread-2
線程名稱:pool-1-thread-4
線程隊列大小為-->4
線程名稱:pool-1-thread-5
線程名稱:pool-1-thread-6
線程名稱:pool-1-thread-5
線程名稱:pool-1-thread-6
線程名稱:pool-1-thread-4
線程名稱:pool-1-thread-2

可見,總共10個線程,因為核心線程數(shù)為2,2個線程被立即運行,線程隊列大小為4,所以4個線程被加入隊列,最大線程數(shù)為6,還能運行6-2=4個,其10個線程的其余4個線程又立即運行了。

如果將我們要運行的線程數(shù)10改為11,則由于最大線程數(shù)6+線程隊列大小4=10<11,則根據(jù)線程池工作原則,最后一個線程將被拒絕策略拒絕,將示例二的main方法改為如下:

public static void main(String[] args) throws InterruptedException { ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(4); //固定為4的線程隊列 ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 6, 1, TimeUnit.DAYS, queue); for (int i = 0; i < 11; i++) { executor.execute(new Thread(new ThreadPoolTest(), "TestThread".concat(""+i))); int threadSize = queue.size(); System.out.println("線程隊列大小為-->"+threadSize); } executor.shutdown(); }

輸出結(jié)果:

?

線程隊列大小為-->0
線程名稱:pool-1-thread-1
線程隊列大小為-->0
線程隊列大小為-->1
線程隊列大小為-->2
線程隊列大小為-->3
線程隊列大小為-->4
線程名稱:pool-1-thread-2
線程隊列大小為-->4
線程名稱:pool-1-thread-3
線程隊列大小為-->4
線程名稱:pool-1-thread-4
線程隊列大小為-->4
線程名稱:pool-1-thread-5
線程隊列大小為-->4
線程名稱:pool-1-thread-6
Exception in thread "main" java.util.concurrent.RejectedExecutionException
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)
at ths.ThreadPoolTest.main(ThreadPoolTest.java:30)
線程名稱:pool-1-thread-1
線程名稱:pool-1-thread-3
線程名稱:pool-1-thread-2
線程名稱:pool-1-thread-4

很明顯,拋RejectedExecutionException異常了,被拒絕策略拒絕了,這就說明線程超出了線程池的總?cè)萘?#xff08;線程隊列大小+最大線程數(shù))。

? ? ? ? ?對于?java.util.concurrent.BlockingQueue?類有有三種方法將線程添加到線程隊列里面,然而如何區(qū)別三種方法的不同呢,其實在隊列未滿的情況下結(jié)果相同,都是將線程添加到線程隊列里面,區(qū)分就在于當線程隊列已經(jīng)滿的時候,此時

public boolean add(E e) 方法將拋出IllegalStateException異常,說明隊列已滿。

public boolean offer(E e) 方法則不會拋異常,只會返回boolean值,告訴你添加成功與否,隊列已滿,當然返回false。

public void put(E e) throws InterruptedException 方法則一直阻塞(即等待,直到線程池中有線程運行完畢,可以加入隊列為止)。

為了證明對上面這三個方法的描述,我們將示例二改為如下、public boolean?add(E e)方法測試程序:

public static void main(String[] args) throws InterruptedException { BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(4); //固定為4的線程隊列 ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 6, 1, TimeUnit.DAYS, queue); for (int i = 0; i < 10; i++) { executor.execute(new Thread(new ThreadPoolTest(), "TestThread".concat(""+i))); int threadSize = queue.size(); System.out.println("線程隊列大小為-->"+threadSize); if (threadSize==4){ queue.add(new Runnable() { //隊列已滿,拋異常 @Override public void run(){ System.out.println("我是新線程,看看能不能搭個車加進去!"); } }); } } executor.shutdown(); }

運行結(jié)果:

?

線程隊列大小為-->0
線程名稱:pool-1-thread-1
線程隊列大小為-->0
線程隊列大小為-->1
線程隊列大小為-->2
線程隊列大小為-->3
線程隊列大小為-->4
線程名稱:pool-1-thread-2
Exception in thread "main" java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(Unknown Source)
at java.util.concurrent.ArrayBlockingQueue.add(Unknown Source)
at ths.ThreadPoolTest.main(ThreadPoolTest.java:35)
線程名稱:pool-1-thread-1
線程名稱:pool-1-thread-2
線程名稱:pool-1-thread-2
線程名稱:pool-1-thread-1

很明顯,當線程隊列已滿,即線程隊列里的線程數(shù)為4時,拋了異常,add線程失敗。再來看public boolean offer(E e) 方法測試程序:

public static void main(String[] args) throws InterruptedException { BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(4); //固定為4的線程隊列 ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 6, 1, TimeUnit.DAYS, queue); for (int i = 0; i < 10; i++) { executor.execute(new Thread(new ThreadPoolTest(), "TestThread".concat(""+i))); int threadSize = queue.size(); System.out.println("線程隊列大小為-->"+threadSize); if (threadSize==4){ final boolean flag = queue.offer(new Runnable() { @Override public void run(){ System.out.println("我是新線程,看看能不能搭個車加進去!"); } }); System.out.println("添加新線程標志為-->"+flag); } } executor.shutdown(); }

運行結(jié)果如下:

?

線程隊列大小為-->0
線程名稱:pool-1-thread-1
線程隊列大小為-->0
線程隊列大小為-->1
線程隊列大小為-->2
線程隊列大小為-->3
線程隊列大小為-->4
添加新線程標志為-->false
線程隊列大小為-->4
線程名稱:pool-1-thread-3
添加新線程標志為-->false
線程名稱:pool-1-thread-2
線程隊列大小為-->4
添加新線程標志為-->false
線程名稱:pool-1-thread-4
線程隊列大小為-->4
添加新線程標志為-->false
線程名稱:pool-1-thread-5
線程隊列大小為-->4
添加新線程標志為-->false
線程名稱:pool-1-thread-6
線程名稱:pool-1-thread-1
線程名稱:pool-1-thread-2
線程名稱:pool-1-thread-3
線程名稱:pool-1-thread-4

可以看到,當線程隊列已滿的時候,線程沒有被添加到線程隊列,程序也沒有拋異常。繼續(xù)看public void put(E e) throws InterruptedException;方法測試程序:

public static void main(String[] args) throws InterruptedException { BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(4); //固定為4的線程隊列 ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 6, 1, TimeUnit.DAYS, queue); for (int i = 0; i < 10; i++) { executor.execute(new Thread(new ThreadPoolTest(), "TestThread".concat(""+i))); int threadSize = queue.size(); System.out.println("線程隊列大小為-->"+threadSize); if (threadSize==4){ queue.put(new Runnable() { @Override public void run(){ System.out.println("我是新線程,看看能不能搭個車加進去!"); } }); } } executor.shutdown(); }

結(jié)果如下:

?

線程隊列大小為-->0
線程隊列大小為-->0
線程隊列大小為-->1
線程隊列大小為-->2
線程隊列大小為-->3
線程隊列大小為-->4
線程名稱:pool-1-thread-1
線程名稱:pool-1-thread-2
線程名稱:pool-1-thread-1
線程名稱:pool-1-thread-2
線程隊列大小為-->3
線程隊列大小為-->4
線程名稱:pool-1-thread-3
線程名稱:pool-1-thread-2
線程隊列大小為-->4
線程名稱:pool-1-thread-1
線程隊列大小為-->4
我是新線程,看看能不能搭個車加進去!
線程名稱:pool-1-thread-3
線程名稱:pool-1-thread-4
我是新線程,看看能不能搭個車加進去!
線程名稱:pool-1-thread-3
我是新線程,看看能不能搭個車加進去!
我是新線程,看看能不能搭個車加進去!

很明顯,嘗試了四次才加進去,前面三次嘗試添加,但由于線程sleep(3000),所以沒有執(zhí)行完,線程隊列一直處于滿的狀態(tài),直到某個線程執(zhí)行完,隊列有空位,新線程才加進去,沒空位之前一直阻塞(即等待),我能加進去為止。

?

那么線程池的排除策略是什么樣呢,一般按如下規(guī)律執(zhí)行:

A. ?如果運行的線程少于 corePoolSize,則 Executor 始終首選添加新的線程,而不進行排隊。
B. ?如果運行的線程等于或多于 corePoolSize,則 Executor 始終首選將請求加入隊列,而不添加新的線程。
C. ?如果無法將請求加入隊列,則創(chuàng)建新的線程,除非創(chuàng)建此線程超出 maximumPoolSize,在這種情況下,任務將被拒絕。

?

總結(jié):

1. 線程池可立即運行的最大線程數(shù) 即maximumPoolSize 參數(shù)。

2. 線程池能包含的最大線程數(shù) =?可立即運行的最大線程數(shù) + 線程隊列大小 (一部分立即運行,一部分裝隊列里等待)

3. 核心線程數(shù)可理解為建議值,即建議使用的線程數(shù),或者依據(jù)CPU核數(shù)

4. add,offer,put三種添加線程到隊列的方法只在隊列滿的時候有區(qū)別,add為拋異常,offer返回boolean值,put直到添加成功為止。

5.同理remove,poll, take三種移除隊列中線程的方法只在隊列為空的時候有區(qū)別, remove為拋異常,poll為返回boolean值, take等待直到有線程可以被移除。

看看下面這張圖就清楚了:

轉(zhuǎn)載于:https://www.cnblogs.com/feiyun126/p/7692068.html

總結(jié)

以上是生活随笔為你收集整理的转:JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。