通过java.util.concurrent写多线程程序
生活随笔
收集整理的這篇文章主要介紹了
通过java.util.concurrent写多线程程序
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
在JDK 1.5之前,要實現(xiàn)多線程的功能,得用到Thread這個類,通過這個類設(shè)計多線程程序,需要考慮性能,死鎖,資源等很多因素,一句話,就是相當麻煩,而且很容易出問題。所幸的是,在JDK1.5之后,java.util.concurrent包出現(xiàn)了,這是一個設(shè)計良好的多線程工具類,本文就將介紹該類的基本使用方法。 按照本博的風格,依然是先扔上一段示例代碼,然后我們再慢慢講解 package com.giantray;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TestConcurrent {public static ExecutorService exec = Executors.newCachedThreadPool();public void startMutilThread(){int taskNum = 2;final CountDownLatch cd = new CountDownLatch(taskNum );//任務(wù)1exec.submit(new Runnable(){@Overridepublic void run(){System. out.println("1 start" );try {Thread. sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}cd.countDown();System. out.println("1 end" );}});//任務(wù)2exec.submit(new Runnable(){@Overridepublic void run(){System. out.println("2 start" );cd.countDown();System. out.println("2 end" );}});System. out.println("submit end" );try{cd.await();}catch (InterruptedException e){}System. out.print("end" );}public static void main(String[] args) {TestConcurrent testThread = new TestConcurrent();testThread.startMutilThread();}
}
輸出 1 start submit end 2 start 2 end 1 end end
下面進行詳細介紹:
2、final CountDownLatch cd = new CountDownLatch(taskNum ); 定義計數(shù)器,指定要并發(fā)執(zhí)行的任務(wù)數(shù)
3、 exec .submit( new Runnable(){};定義任務(wù)的執(zhí)行邏輯
4、cd.countDown();任務(wù)結(jié)束了,要讓計算器減1
5、cd.await();表示等待計數(shù)器小于等于0時,再繼續(xù)往下走其他邏輯
1、newFixedThreadPool(固定大小線程池)
線程數(shù)是固定的,例如線程數(shù)設(shè)為1,則需要等待任務(wù)1結(jié)束后,才執(zhí)行任務(wù)2;如設(shè)為2,則兩個任務(wù)能同時執(zhí)行
2、newCachedThreadPool(無限制大小的線程池)
線程數(shù)無固定大小,當前無可用線程,就回創(chuàng)建新線程。如果任務(wù)1沒結(jié)束,那么啟動任務(wù)2時,就會新建線程,這時線程池大小為2;但假如啟動任務(wù)2時,任務(wù)1已結(jié)束,則不會新建線程,會沿用任務(wù)1使用的線程
3、newSingleThreadExecutor(單線程)
使用單線程池時,就沒辦法同時執(zhí)行多個任務(wù),因此,本例中的任務(wù)1執(zhí)行完后,才會執(zhí)行任務(wù)2
4、newScheduledThreadPool(定時任務(wù)線程池)
支持定時任務(wù)的線程池,與Timer,Quartz一樣,常見于一些定時任務(wù)調(diào)度程序·
shutdown() 關(guān)閉線程池,但之前提交的任務(wù),會繼續(xù)執(zhí)行;如果新提交任務(wù),會拋異常
shutdownNow() 馬上關(guān)閉線程池,之前提交的任務(wù),如果還未執(zhí)行完,會被終止,且拋出異常
對示例代碼做下修改,其中,task1.get()將拿到call()方法的return值,也就是示例中的"false"
int taskNum = 1; final CountDownLatch cd = new CountDownLatch(taskNum);Future<Boolean> task1 = exec.submit(new Callable<Boolean>(){@Overridepublic Boolean call() throws Exception {System.out.println("1 start");cd.countDown();System.out.println("1 end");return false;}});try {cd.await();Boolean result = task1.get();System.out.println("result : " + result); } catch (InterruptedException e) {e.printStackTrace(); } catch (ExecutionException e) {e.printStackTrace(); } System.out.print("end");
輸出 1 start submit end 2 start 2 end 1 end end
下面進行詳細介紹:
一、使用流程
1、ExecutorService exec = ?Executors.newCachedThreadPool();獲得一個線程池2、final CountDownLatch cd = new CountDownLatch(taskNum ); 定義計數(shù)器,指定要并發(fā)執(zhí)行的任務(wù)數(shù)
3、 exec .submit( new Runnable(){};定義任務(wù)的執(zhí)行邏輯
4、cd.countDown();任務(wù)結(jié)束了,要讓計算器減1
5、cd.await();表示等待計數(shù)器小于等于0時,再繼續(xù)往下走其他邏輯
二、線程池的管理
除了示例中的newCachedThreadPool(),也有其他各種類型的線程池:1、newFixedThreadPool(固定大小線程池)
線程數(shù)是固定的,例如線程數(shù)設(shè)為1,則需要等待任務(wù)1結(jié)束后,才執(zhí)行任務(wù)2;如設(shè)為2,則兩個任務(wù)能同時執(zhí)行
2、newCachedThreadPool(無限制大小的線程池)
線程數(shù)無固定大小,當前無可用線程,就回創(chuàng)建新線程。如果任務(wù)1沒結(jié)束,那么啟動任務(wù)2時,就會新建線程,這時線程池大小為2;但假如啟動任務(wù)2時,任務(wù)1已結(jié)束,則不會新建線程,會沿用任務(wù)1使用的線程
3、newSingleThreadExecutor(單線程)
使用單線程池時,就沒辦法同時執(zhí)行多個任務(wù),因此,本例中的任務(wù)1執(zhí)行完后,才會執(zhí)行任務(wù)2
4、newScheduledThreadPool(定時任務(wù)線程池)
支持定時任務(wù)的線程池,與Timer,Quartz一樣,常見于一些定時任務(wù)調(diào)度程序·
三、計數(shù)器CountDownLatch
1、示例講解
在示例中,我們通過多線程執(zhí)行了兩個任務(wù)。之后我們需要知道,兩個任務(wù)是否都執(zhí)行結(jié)束?都執(zhí)行結(jié)束了,我們才能繼續(xù)執(zhí)行接下來的其他邏輯。為了這個目的,需要引入CountDownLatch這個計數(shù)器。先聲明一個計算量為TaskNum(2)的計數(shù)器,接著,每個任務(wù)執(zhí)行結(jié)束時,要執(zhí)行countDown()方法,而最后的await()方法表示,如果CountDownLatch不為零,那么就在這里等待,下面的邏輯會被阻塞住,不會繼續(xù)執(zhí)行,等待兩個任務(wù)都執(zhí)行結(jié)束了--執(zhí)行兩次countDown()方法,計數(shù)器等于0,這時候程序才會繼續(xù)往下執(zhí)行System. out.print("end")這行代碼2、它的兄弟CyclicBarrier
還有一個類似CountDownLatch的計數(shù)器,名為CyclicBarrier。這兩者的區(qū)別:CountDownLatch被減至0后,是不能重置的。而CyclicBarrier被減至0后,會自動恢復(fù)至初始值,因此它是一個循環(huán)的計數(shù)器,舉一個例子,運動員跑步,這是一個任務(wù),而這個任務(wù),又分為運動員準備,以及開跑兩個子任務(wù),所有的運動員,必須等待其他運動員都”準備“好后,才能起跑。為了實現(xiàn)這個邏輯,及可以用到CyclicBarrier,CyclicBarrier的計數(shù)初始值為運動員數(shù)量,運動員準備完畢時,讓計數(shù)器減1,這時候它不會進行”開跑“,等到計數(shù)器減為0時,所有運動員才同時“起跑”,并且會重新恢復(fù)至初始值,等到所有運動員都跑完時,計數(shù)器才又恢復(fù)為0,這時候,才能再繼續(xù)執(zhí)行其他任務(wù)。而如果使用CyclicBarrier,在線程執(zhí)行過程中,是不能等待其他線程的,不能等其他線程都“準備”好了,所有線程再一起“開跑”四、并發(fā)執(zhí)行
多運行幾次示例程序,會發(fā)現(xiàn),有時候,"2 start"會早于"1 start"輸出,因此,不一定是先submit的任務(wù),就先“開始執(zhí)行”。這也告訴我們,不能想當然地以為,任務(wù)1先submit,就可以在任務(wù)1里做一些全局初始化的工作,然后在任務(wù)2里可以去拿任務(wù)1初始化的變量。五、線程池的生命周期管理
1、三種基本狀態(tài)
線程池的聲明周期有三個狀態(tài),運行,關(guān)閉,終止。初始化線程時,處于運行狀態(tài),執(zhí)行shutdown()方法后,處于關(guān)閉狀態(tài),但這時候線程任務(wù)會繼續(xù)執(zhí)行,當所有任務(wù)都結(jié)束后,才會變成終止狀態(tài)。2、shutdown()與shutdownNow()的區(qū)別
這兩個方法都用于關(guān)閉線程池shutdown() 關(guān)閉線程池,但之前提交的任務(wù),會繼續(xù)執(zhí)行;如果新提交任務(wù),會拋異常
shutdownNow() 馬上關(guān)閉線程池,之前提交的任務(wù),如果還未執(zhí)行完,會被終止,且拋出異常
3、shutdown()的作用
- ? 釋放線程池資源(其實不關(guān)閉線程池,會資源會有多大的消耗,筆者也不清楚)
- ? 可以讓程序有序退出。這是什么意思呢?講一個例子,你就明白了。假如你的程序正在并發(fā)執(zhí)行任務(wù),而且還一直會提交新的任務(wù)。這時候,該如何設(shè)計程序的“退出”功能呢?首先,為了任務(wù)的原子性,希望能執(zhí)行完已提交的任務(wù),并且阻止新的任務(wù)提交。這時候,就得用到了shutdown()了。退出功能的代碼可以是這樣:
六、獲取線程任務(wù)的執(zhí)行結(jié)果
如果想知道任務(wù)的執(zhí)行結(jié)果,就得使用Future和Callable。Future可以拿到結(jié)果,而Callable能返回值,用于產(chǎn)生結(jié)果對示例代碼做下修改,其中,task1.get()將拿到call()方法的return值,也就是示例中的"false"
int taskNum = 1; final CountDownLatch cd = new CountDownLatch(taskNum);Future<Boolean> task1 = exec.submit(new Callable<Boolean>(){@Overridepublic Boolean call() throws Exception {System.out.println("1 start");cd.countDown();System.out.println("1 end");return false;}});try {cd.await();Boolean result = task1.get();System.out.println("result : " + result); } catch (InterruptedException e) {e.printStackTrace(); } catch (ExecutionException e) {e.printStackTrace(); } System.out.print("end");
?
總結(jié)
以上是生活随笔為你收集整理的通过java.util.concurrent写多线程程序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DHTML【7】--CSS
- 下一篇: InnoDB和MyISAM的区别与选择