首先說明下java線程是如何實現線程重用的
公司主營業務:網站制作、成都網站設計、移動網站開發等業務。幫助企業客戶真正實現互聯網宣傳,提高企業的競爭能力。創新互聯是一支青春激揚、勤奮敬業、活力青春激揚、勤奮敬業、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業文化,感謝他們對我們的高要求,感謝他們從不同領域給我們帶來的挑戰,讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創新互聯推出儀征免費做網站回饋大家。
1. 線程執行完一個Runnable的run()方法后,不會被殺死
2. 當線程被重用時,這個線程會進入新Runnable對象的run()方法12
java線程池由Executors提供的幾種靜態方法創建線程池。下面通過代碼片段簡單介紹下線程池的幾種實現方式。后續會針對每個實現方式做詳細的說明
newFixedThreadPool
創建一個固定大小的線程池
添加的任務達到線程池的容量之后開始加入任務隊列開始線程重用總共開啟線程個數跟指定容量相同。
@Test
public void newFixedThreadPool() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService = Executors.newFixedThreadPool(1, new ThreadFactoryBuilder().build());
RunThread run1 = new RunThread("run 1");
executorService.execute(run1);
executorService.shutdown();
}12345678
newSingleThreadExecutor
僅支持單線程順序處理任務
@Test
public void newSingleThreadExecutor() throws Exception {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().build());
executorService.execute(new RunThread("run 1"));
executorService.execute(new RunThread("run 2"));
executorService.shutdown();
}123456789
newCachedThreadPool
這種情況跟第一種的方式類似,不同的是這種情況線程池容量上線是Integer.MAX_VALUE 并且線程池開啟緩存60s
@Test
public void newCachedThreadPool() throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService = Executors.newCachedThreadPool(new ThreadFactoryBuilder().build());
executorService.execute(new RunThread("run 1"));
executorService.execute(new RunThread("run 2"));
executorService.shutdown();
}123456789
newWorkStealingPool
支持給定的并行級別,并且可以使用多個隊列來減少爭用。
@Test
public void newWorkStealingPool() throws Exception {
ExecutorService executorService = Executors.newWorkStealingPool();
executorService = Executors.newWorkStealingPool(1);
RunThread run1 = new RunThread("run 1");
executorService.execute(run1);
executorService.shutdown();
}123456789
newScheduledThreadPool
看到的現象和第一種相同,也是在線程池滿之前是新建線程,然后開始進入任務隊列,進行線程重用
支持定時周期執行任務(還沒有看完)
@Test
public void newScheduledThreadPool() throws Exception {
ExecutorService executorService = Executors.newScheduledThreadPool(1);
executorService = Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder().build());
executorService.execute(new RunThread("run 1"));
executorService.execute(new RunThread("run 2"));
executorService.shutdown();
}
多線程環境中,通過隊列可以很容易實現線程間數據共享,比如經典的“生產者”和“消費者”模型中,通過隊列可以很便利地實現兩者之間的數據共享;同時作為BlockingQueue的使用者,我們不需要關心什么時候需要阻塞線程,什么時候需要喚醒線程,因為這一切BlockingQueue的實現者都給一手包辦了。
基于數組的阻塞隊列實現,在ArrayBlockingQueue內部,維護了一個定長數組,以便緩存隊列中的數據對象,另外還保存著兩個整形變量,分別標識著隊列的頭部和尾部在數組中的位置。
ArrayBlockingQueue在生產者放入數據和消費者獲取數據,都是共用同一個鎖對象,由此也意味著兩者無法真正并行運行,而在創建ArrayBlockingQueue時,我們還可以控制對象的內部鎖是否采用公平鎖,默認采用非公平鎖。
按照實現原理來分析,ArrayBlockingQueue完全可以采用分離鎖,從而實現生產者和消費者操作的完全并行運行。
基于鏈表的阻塞隊列,其內部也維持著一個數據緩沖隊列(由一個鏈表構成),當生產者往隊列中放入一個數據時,隊列會從生產者手中獲取數據,并緩存在隊列內部,而生產者立即返回;只有當隊列緩沖區達到最大值緩存容量時(LinkedBlockingQueue可以通過構造函數指定該值),才會阻塞生產者隊列,直到消費者從隊列中消費掉一份數據,生產者線程會被喚醒,反之對于消費者這端的處理也基于同樣的原理。
對于生產者端和消費者端分別采用了獨立的鎖來控制數據同步,這也意味著在高并發的情況下生產者和消費者可以并行地操作隊列中的數據,以此來提高整個隊列的并發性能。
ArrayBlockingQueue和LinkedBlockingQueue間還有一個明顯的不同之處在于,前者在插入或刪除元素時不會產生或銷毀任何額外的對象實例,而后者則會生成一個額外的Node對象。這在長時間內需要高效并發地處理大批量數據的系統中,其對于GC的影響還是存在一定的區別。如果沒有指定其容量大小,LinkedBlockingQueue會默認一個類似無限大小的容量(Integer.MAX_VALUE),這樣的話,如果生產者的速度一旦大于消費者的速度,也許還沒有等到隊列滿阻塞產生,系統內存就有可能已被消耗殆盡了。
ArrayBlockingQueue和LinkedBlockingQueue是兩個最普通也是最常用的阻塞隊列,一般情況下,在處理多線程間的生產者消費者問題,使用這兩個類足以。
DelayQueue中的元素只有當其指定的延遲時間到了,才能夠從隊列中獲取到該元素。DelayQueue是一個沒有大小限制的隊列,因此往隊列中插入數據的操作(生產者)永遠不會被阻塞,而只有獲取數據的操作(消費者)才會被阻塞。
DelayQueue用于放置實現了Delayed接口的對象,其中的對象只能在其到期時才能從隊列中取走。這種隊列是有序的,即隊頭對象的延遲到期時間最長。注意:不能將null元素放置到這種隊列中。
Delayed 是一種混合風格的接口,用來標記那些應該在給定延遲時間之后執行的對象。Delayed擴展了Comparable接口,比較的基準為延時的時間值,Delayed接口的實現類getDelay的返回值應為固定值(final)。DelayQueue內部是使用PriorityQueue實現的。
考慮以下場景:
一種笨笨的辦法就是,使用一個后臺線程,遍歷所有對象,挨個檢查。這種笨笨的辦法簡單好用,但是對象數量過多時,可能存在性能問題,檢查間隔時間不好設置,間隔時間過大,影響精確度,多小則存在效率問題。而且做不到按超時的時間順序處理。
這場景,使用DelayQueue最適合了,詳情查看 DelayedQueue學習筆記 ; 精巧好用的DelayQueue
基于優先級的阻塞隊列(優先級的判斷通過構造函數傳入的Compator對象來決定),需要注意PriorityBlockingQueue并不會阻塞數據生產者,而只會在沒有可消費的數據時,阻塞數據的消費者。
使用時,若生產者生產數據的速度快于消費者消費數據的速度,隨著長時間的運行,可能會耗盡所有的可用堆內存空間。在實現PriorityBlockingQueue時,內部控制線程同步的鎖采用的是公平鎖。
SynchronousQueue是一個內部只能包含零個元素的隊列。插入元素到隊列的線程被阻塞,直到另一個線程從隊列中獲取元素。同樣,如果線程嘗試獲取元素并且當前沒有線程在插入元素,則該線程將被阻塞,直到有線程將元素插入隊列
聲明一個SynchronousQueue有公平模式和非公平模式,區別如下:
參考: Java多線程-工具篇-BlockingQueue
12. SynchronousQueue
java中的消息隊列
消息隊列是線程間通訊的手段:
import java.util.*
public class MsgQueue{
private Vector queue = null;
public MsgQueue(){
queue = new Vector();
}
public synchronized void send(Object o)
{
queue.addElement(o);
}
public synchronized Object recv()
{
if(queue.size()==0)
return null;
Object o = queue.firstElement();
queue.removeElementAt(0);//or queue[0] = null can also work
return o;
}
}
因為java中是locked by object的所以添加synchronized 就可以用于線程同步鎖定對象
可以作為多線程處理多任務的存放task的隊列。他的client包括封裝好的task類以及thread類
Java的多線程-線程間的通信2009-08-25 21:58
1. 線程的幾種狀態
線程有四種狀態,任何一個線程肯定處于這四種狀態中的一種:
1) 產生(New):線程對象已經產生,但尚未被啟動,所以無法執行。如通過new產生了一個線程對象后沒對它調用start()函數之前。
2) 可執行(Runnable):每個支持多線程的系統都有一個排程器,排程器會從線程池中選擇一個線程并啟動它。當一個線程處于可執行狀態時,表示它可能正處于線程池中等待排排程器啟動它;也可能它已正在執行。如執行了一個線程對象的start()方法后,線程就處于可執行狀態,但顯而易見的是此時線程不一定正在執行中。
3) 死亡(Dead):當一個線程正常結束,它便處于死亡狀態。如一個線程的run()函數執行完畢后線程就進入死亡狀態。
4) 停滯(Blocked):當一個線程處于停滯狀態時,系統排程器就會忽略它,不對它進行排程。當處于停滯狀態的線程重新回到可執行狀態時,它有可能重新執行。如通過對一個線程調用wait()函數后,線程就進入停滯狀態,只有當兩次對該線程調用notify或notifyAll后它才能兩次回到可執行狀態。
2. class Thread下的常用函數函數
2.1 suspend()、resume()
1) 通過suspend()函數,可使線程進入停滯狀態。通過suspend()使線程進入停滯狀態后,除非收到resume()消息,否則該線程不會變回可執行狀態。
2) 當調用suspend()函數后,線程不會釋放它的“鎖標志”。
例11:
class TestThreadMethod extends Thread{
public static int shareVar = 0;
public TestThreadMethod(String name){
super(name);
}
public synchronized void run(){
if(shareVar==0){
for(int i=0; i5; i++){
shareVar++;
if(shareVar==5){
this.suspend(); //(1)
}}}
else{
System.out.print(Thread.currentThread().getName());
System.out.println(" shareVar = " + shareVar);
this.resume(); //(2)
}}
}
public class TestThread{
public static void main(String[] args){
TestThreadMethod t1 = new TestThreadMethod("t1");
TestThreadMethod t2 = new TestThreadMethod("t2");
t1.start(); //(5)
//t1.start(); //(3)
t2.start(); //(4)
}}
創建一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。在任意點,在大多數 nThreads 線程會處于處理任務的活動狀態。如果在所有線程處于活動狀態時提交附加任務,則在有可用線程之前,附加任務將在隊列中等待。如果在關閉前的執行期間由于失敗而導致任何線程終止,那么一個新線程將代替它執行后續的任務(如果需要)。在某個線程被顯式地關閉之前,池中的線程將一直存在。
看代碼:
Java代碼 收藏代碼
package test;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ExecutorTest {
public static void main(String args[]) {
Random random = new Random();
//產生一個 ExecutorService 對象,這個對象帶有一個大小為 poolSize 的線程池,若任務數量大于 poolSize ,任務會被放在一個 queue 里順序執行。
ExecutorService executor = Executors.newFixedThreadPool(3);
// 判斷可是線程池可以結束
int waitTime = 500;
for (int i = 0; i 10; i++) {
String name = "線程 " + i;
int time = random.nextInt(1000);
waitTime += time;
Runnable runner = new ExecutorThread(name, time);
System.out.println("增加: " + name + " / " + time);
executor.execute(runner);
}
try {
Thread.sleep(waitTime);
executor.shutdown();
executor.awaitTermination(waitTime, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) {
}
}
}
class ExecutorThread implements Runnable {
private final String name;
private final int delay;
public ExecutorThread(String name, int delay) {
this.name = name;
this.delay = delay;
}
public void run() {
System.out.println("啟動: " + name);
try {
Thread.sleep(delay);
} catch (InterruptedException ignored) {
}
System.out.println("完成: " + name);
}
}
分享標題:java線程池隊列代碼,Java線程池代碼
分享路徑:http://vcdvsql.cn/article24/hedece.html
成都網站建設公司_創新互聯,為您提供移動網站建設、品牌網站設計、微信小程序、營銷型網站建設、App開發、面包屑導航
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯