1. 進程是資源分配的最小單位,線程是CPU調度的最小單位java
2. 一個進程由一個或多個線程組成mysql
3. 進程之間相互獨立,每一個進程都有獨立的代碼和數據空間,但同一進程下的各個線程之間共享進程的代碼和內存空間,每一個線程有獨立的運行棧和程序計數器sql
4. 線程上下文切換比進程上下文切換要快得多數據庫
在java中要想實現多線程,有兩種手段,一種是繼續Thread類(extends )緩存
另一種是實現Runable接口(implements ,須要先經過Thread類的構造方法Thread(Runnable target) 構造出對象)。安全
實現runnable接口的優點:服務器
適合於資源的共享多線程
能夠避免java中的單繼承的限制併發
增長程序的健壯性,代碼能夠被多個線程共享ide
新建狀態(New):新建立了一個線程對象。
就緒狀態(Runnable):線程對象建立後,其餘線程調用了該對象的start()方法。變得可運行,等待獲取CPU的使用權。
運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。
阻塞狀態(Blocked):阻塞狀態是線程由於某種緣由放棄CPU使用權,暫時中止運行。
死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
1 線程間有數據共享,而且數據是須要修改的(不一樣任務間須要大量共享數據或頻繁通訊時);
2 提供非均質的服務(有優先級任務處理)事件響應有優先級;
3 單任務並行計算,提升響應速度,下降時延;
4 與人有IO交互的應用,良好的用戶體驗(鍵盤鼠標的輸入,馬上響應)
1. 作WEB,主線程專門監聽用戶的HTTP請求,而後啓動子線程去處理用戶的HTTP請求。提升吞吐量
2.
某種任務,雖然耗時,可是不耗CPU的操做時,開啓多個線程,效率會有顯著提升。
好比讀取文件,而後處理。 磁盤IO是個很耗費時間,可是不耗CPU計算的工做。 因此能夠一個線程讀取數據,一個線程處理數據。確定比
3. 數據庫操做
產生緣由:
互斥條件:一個資源每次只能被一個進程使用。
不剝奪條件:進程已得到的資源,在末使用完以前,不能強行剝奪。
請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。
循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。
如何避免死鎖:
加鎖順序(線程按照必定的順序加鎖,只有得到了從順序上排在前面的鎖以後,才能獲取後面的鎖。與解鎖順序無關)
加鎖時限(線程嘗試獲取鎖的時候加上必定的時限,超過期限則放棄對該鎖的請求,並釋放本身佔有的鎖,而後等待一段隨機的時間再重試)
死鎖檢測
原子操做:由一組相關的操做完成,這些操做可能會操縱與其它的線程共享的資源,爲了保證獲得正確的運算結果,一個線程在執行原子操做其間,應該採起其餘的措施使得其餘的線程不能操縱共享資源。
1. sleep(long millis): 在指定的毫秒數內讓當前正在執行的線程休眠,進入阻塞狀態,不會釋放鎖
2. join():當前線程進入阻塞狀態,等待加入線程終止後才能執行。
3. setPriority(): 更改線程的優先級。
4. setName(): 爲線程設置一個名稱。
5. interrupt():中斷某個線程,這種結束方式比較粗暴,若是t線程打開了某個資源還沒來得及關閉也就是run方法尚未執行完就強制結束線程,會致使資源沒法關閉
6. wait()、Obj.wait()、Obj.notify()
必需要與synchronized(Obj)一塊兒使用,也就是wait,與notify是針對已經獲取了Obj鎖進行操做,從語法角度來講就是Obj.wait(),Obj.notify必須在synchronized(Obj){...}語句塊內。從功能上來講wait就是說線程在獲取對象鎖後,主動釋放對象鎖,同時本線程休眠。直到有其它線程調用對象的notify()喚醒該線程,才能繼續獲取對象鎖,並繼續執行。相應的notify()就是對對象鎖的喚醒操做。但有一點須要注意的是notify()調用後,並非立刻就釋放對象鎖的,而是在相應的synchronized(){}語句塊執行結束,自動釋放鎖後,JVM會在wait()對象鎖的線程中隨機選取一線程,賦予其對象鎖,喚醒線程,繼續執行。這樣就提供了在線程間同步、喚醒的操做。Thread.sleep()與Object.wait()兩者均可以暫停當前線程,釋放CPU控制權,主要的區別在於Object.wait()在釋放CPU同時,釋放了對象鎖的控制。
wait和sleep區別
sleep()睡眠時,保持對象鎖,仍然佔有該鎖;是thread的方法
而wait()睡眠時,釋放對象鎖。是object的方法
線程安全:就是說多線程訪問同一代碼,不會產生不肯定的結果。
1. synchronized同步方法
即有synchronized關鍵字修飾的方法。因爲java的每一個對象都有一個內置鎖,當用此關鍵字修飾方法時,內置鎖會保護整個方法。
也能夠修飾靜態方法,此時若是調用該靜態方法,將會鎖住整個類
2. synchronized同步代碼塊
即有synchronized關鍵字修飾的語句塊。
當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就得到了這個object的對象鎖。結果,其它線程對該object對象全部同步代碼部分的訪問都被暫時阻塞,但仍然能夠訪問該object中的非synchronized(this)同步代碼塊。
3. volatile實現線程同步
用volatile修飾的變量,線程在每次使用變量的時候,都會從主存中讀取變量最新值。變量修改後會直接改變主存內容。保證可見性,不能保證原子性
4. 使用重入鎖實現線程同步ReentrantLock
ReentrantLock 擁有Synchronized相同的併發性和內存語義,此外還多了 鎖投票,定時鎖等候和中斷鎖等候,好比能夠放棄鎖等待先作別的事情(trylock),而Synchronized不能
synchronized是在JVM層面上實現的,JVM會自動釋放鎖定,可是使用Lock則不行,lock是經過代碼實現的,要保證鎖定必定會被釋放,就必須將unLock()放到finally{}中
在資源競爭很激烈的狀況下,ReetrantLock的性能要優於Synchronized
5. 使用ThreadLocal管理變量
使用該變量的線程都得到該變量的副本,副本之間相互獨立,這樣每個線程均可以隨意修改本身的變量副本,而不會對其餘線程產生影響。
吞吐量:單位時間內成功地傳送數據的數量
1. synchronied關鍵字wait()/notify()、notifyAll()機制:
2. 條件對象的等待/通知機制(await()、signal()、signalAll()):所謂的條件對象也就是配合前面咱們分析的Lock鎖對象,經過鎖對象的條件對象來實現等待/通知機制Condition conditionObj=ticketLock.newCondition()
3. 管道通訊
經過管道,將一個線程中的二進制數據消息發送給另外一個。
進程間通訊:
1.管道
匿名管道:管道是一種半雙工的通訊方式,數據只能單向流動,並且只能在具備親緣關係(父子)的進程間使用。
有名管道: 有名管道也是半雙工的通訊方式,可是它容許無親緣關係進程間的通訊。
2.信號量: 信號量是一個計數器,能夠用來控制多個進程對共享資源的訪問。
3.消息隊列: 消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流
4.信號:用於通知接收進程某個事件已經發生。
5 .共享內存:共享內存是最快的一種 IPC,由於進程是直接對內存進行存取。共享內存就是一段能被其餘進程所訪問的內存,這段共享內存由一個進程建立,但多個進程均可以訪問,信號量+共享內存一般結合在一塊兒使用,來達到進程間的同步與互斥。
4.1 什麼是線程池?
線程池是一個線程集合,而後在須要執行新的任務時重用這些線程而不是新建一個線程。線程池中的每一個線程都有被分配一個任務,一旦任務已經完成了,線程回到池子中並等待下一次分配任務。
4.2 好處
第一:下降資源消耗。經過重複利用已建立的線程下降線程建立和銷燬形成的消耗。
第二:提升響應速度。當任務到達時,任務能夠不須要的等到線程建立就能當即執行。
第三:提升線程的可管理性。
4.3 適用場合?
當一個Web服務器接受到大量短小線程的請求時,使用線程池技術是很是合適的,它能夠大大減小線程的建立和銷燬次數,提升服務器的工做效率。但若是線程要求的運行時間比較長,此時線程的運行時間比建立時間要長得多,單靠減小建立時間對系統效率的提升不明顯,此時就不適合應用線程池技術,須要藉助其它的技術來提升服務器的服務效率。
4.4 建立線程池四種方式
咱們能夠經過Executors工具類的靜態方法來建立線程池。
1. newFixedThreadPool(int nThreads)
建立一個固定長度的線程池,每當提交一個任務就建立一個線程,直到達到線程池的最大數量,這時線程規模將再也不變化
2. newCachedThreadPool()
建立一個可緩存的線程池,適當狀況下可回收添加線程
3. newSingleThreadExecutor()
這是一個單線程的Executor
4. newScheduledThreadPool(int corePoolSize)
建立了一個固定長度的線程池,並且以延遲或定時的方式來執行任務,相似於Timer。
Executors 類使用 ExecutorService 提供了一個 ThreadPoolExecutor 的簡單實現,但 ThreadPoolExecutor 提供的功能遠不止這些。
ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(
corePoolSize,// 核心線程數,當提交一個任務到線程池時,線程池會建立一個線程來執行任務,即便其餘空閒的基本線程可以執行新任務也會建立線程,等到須要執行的任務數大於線程池基本大小時就再也不建立。
maximumPoolSize, // 最大線程數 ,線程池容許建立的最大線程數。若是隊列滿了,而且已建立的線程數小於最大線程數,則線程池會再建立新的線程執行任務。
keepAliveTime, // 線程活動保持時間,閒置線程存活時間 當線程空閒時間達到keepAliveTime,該線程會退出,直到線程數量等於corePoolSize。此時不會退出線程
TimeUnit.MILLISECONDS,// 時間單位,此處爲毫秒
runnableTaskQueuenew ,// 任務隊列,線程隊列用於保存執行任務的阻塞隊列
Executors.defaultThreadFactory(),// 線程工廠
RejectedExecutionHandler// 飽和策略,當隊列和線程池都滿了,說明線程池處於飽和狀態,那麼必須採起一種策略處理提交的新任務。這個策略默認狀況下是AbortPolicy,表示沒法處理新任務時拋出異常。
);
4.5 線程池的處理流程
1. 首先線程池判斷基本線程池是否已滿?沒滿,建立一個工做線程來執行任務。滿了,則進入下個流程。
2. 其次線程池判斷工做隊列是否已滿?沒滿,則將新提交的任務存儲在工做隊列裏。滿了,則進入下個流程。
3. 最後線程池判斷整個線程池是否已滿?沒滿,則建立一個新的工做線程來執行任務,滿了,則交給飽和策略來處理這個任務。
4.6 線程池組成
線程池管理器(ThreadPool):用於建立並管理線程池,包括 建立線程池,銷燬線程池,添加新任務;
工做線程(PoolWorker):線程池中線程,在沒有任務時處於等待狀態,能夠循環的執行任務;
任務隊列(taskQueue):用於存放沒有處理的任務。提供一種緩衝機制。
任務接口(Task):每一個任務必須實現的接口,以供工做線程調度任務的執行,它主要規定了任務的入口,任務執行完後的收尾工做,任務的執行狀態等;
4.7 合理的配置線程池
能夠從如下幾個角度來進行分析:
1. 任務的性質:CPU密集型任務、IO密集型任務 。
任務性質不一樣的任務能夠用不一樣規模的線程池分開處理。CPU密集型任務配置儘量少的線程數量。IO密集型任務則因爲須要等待IO操做,線程並非一直在執行任務,則配置儘量多的線程。
2. 任務的優先級:高,中和低。
優先級不一樣的任務可使用優先級隊列PriorityBlockingQueue來處理。(任務隊列裏的一種)
3. 任務的執行時間:長,中和短。
可使用優先級隊列,讓執行時間短的任務先執行。
4. 任務的依賴性:是否依賴其餘系統資源,如數據庫鏈接。
如依賴數據庫鏈接池的任務,由於線程提交SQL後須要等待數據庫返回結果,若是等待的時間越長CPU空閒時間就越長,那麼線程數應該設置越大,這樣才能更好的利用CPU。
public class MyThreadPrinter2 implements Runnable {
private String name;
private Object prev;
private Object self;
private MyThreadPrinter2(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);
new Thread(pa).start();
Thread.sleep(100); //確保按順序A、B、C執行
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
Thread.sleep(100);
}
}
主要的思想就是,爲了控制執行的順序,必需要先持有prev鎖,也就前一個線程要釋放自身對象鎖,再去申請自身對象鎖,二者兼備時打印,以後首先調用self.notify()釋放自身對象鎖,喚醒下一個等待線程,再調用prev.wait()釋放prev對象鎖,等待下次獲取prev鎖後運行,終止當前線程,等待循環結束後再次被喚醒。
特徵:
單例類只能有一個實例。
單例類必須本身建立本身的惟一實例。
單例類必須給全部其餘對象提供這一實例。
懶漢式單例:
Public class Singleton {
Private Singleton (){};
Private static Singleton single = null;
Public static Singleton getInstance(){
If(Singleton == null)
Single = new Singleton ();
return single;
}
}
可是以上懶漢式單例的實現沒有考慮線程安全問題,它是線程不安全的,併發環境下極可能出現多個Singleton實例,要實現線程安全,有如下三種方式
方法一:在getInstance方法上加同步
public static synchronized Singleton getInstance(){
If(Single == null)
Single = new Singleton ();
return single;
}
方法二 : 雙檢索
public static Singleton getInstance(){
If(single == null){
Synchronized(Singleton.class){
if(single == null)
Single = new Singleton ();
}
}
return single;
}
方法三:靜態內部類
餓漢式單例:
Public class singleton{
Private singleton(){}
Private static final singleton single = new singleton();
Public static singleton getInstance(){
return single;
}
}
單生產者單消費者模式:
1. public class KaoYaResource {
2.
3. private String name;
4. private int count = 1;//烤鴨的初始數量
5. private boolean flag = false;//判斷是否有須要線程等待的標誌
6. public synchronized void product(String name){
7. if(flag){
8. //此時有烤鴨,等待
9. try {
10. this.wait();
11. } catch (InterruptedException e) {
12. e.printStackTrace();
13. }
14. }
15. this.name=name+count;//設置烤鴨的名稱
16. count++;
17. System.out.println(Thread.currentThread().getName()+"...生產者..."+this.name);
18. flag=true;//有烤鴨後改變標誌
19. notifyAll();//通知消費線程能夠消費了
20. }
21. public synchronized void consume(){
22. if(!flag){//若是沒有烤鴨就等待
23. try{this.wait();}catch(InterruptedException e){}
24. }
25. System.out.println(Thread.currentThread().getName()+"...消費者........"+this.name);//消費烤鴨1
26. flag = false;
27. notifyAll();//通知生產者生產烤鴨
28. }
29. }
30.
1. public class Single_Producer_Consumer {
2. public static void main(String[] args)
3. {
4. KaoYaResource r = new KaoYaResource();
5. Producer pro = new Producer(r);
6. Consumer con = new Consumer(r);
7. //生產者線程
8. Thread t0 = new Thread(pro);
9. //消費者線程
10. Thread t2 = new Thread(con);
11. //啓動線程
12. t0.start();
13. t2.start();
14. }
15. }
16. class Producer implements Runnable
17. {
18. private KaoYaResource r;
19. Producer(KaoYaResource r)
20. {
21. this.r = r;
22. }
23. public void run()
24. {
25. while(true)
26. {
27. r.product("北京烤鴨");
28. }
29. }
30. }
31. class Consumer implements Runnable
32. {
33. private KaoYaResource r;
34. Consumer(KaoYaResource r)
35. {
36. this.r = r;
37. }
38. public void run()
39. {
40. while(true)
41. {
42. r.consume();
43. }
44. }
}
1. 進程是資源分配的最小單位,線程是CPU調度的最小單位
2. 一個進程由一個或多個線程組成
3. 進程之間相互獨立,每一個進程都有獨立的代碼和數據空間,但同一進程下的各個線程之間共享進程的代碼和內存空間,每一個線程有獨立的運行棧和程序計數器
4. 線程上下文切換比進程上下文切換要快得多
在java中要想實現多線程,有兩種手段,一種是繼續Thread類(extends )
另一種是實現Runable接口(implements ,須要先經過Thread類的構造方法Thread(Runnable target) 構造出對象)。
實現runnable接口的優點:
適合於資源的共享
能夠避免java中的單繼承的限制
增長程序的健壯性,代碼能夠被多個線程共享
新建狀態(New):新建立了一個線程對象。
就緒狀態(Runnable):線程對象建立後,其餘線程調用了該對象的start()方法。變得可運行,等待獲取CPU的使用權。
運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。
阻塞狀態(Blocked):阻塞狀態是線程由於某種緣由放棄CPU使用權,暫時中止運行。
死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
1 線程間有數據共享,而且數據是須要修改的(不一樣任務間須要大量共享數據或頻繁通訊時);
2 提供非均質的服務(有優先級任務處理)事件響應有優先級;
3 單任務並行計算,提升響應速度,下降時延;
4 與人有IO交互的應用,良好的用戶體驗(鍵盤鼠標的輸入,馬上響應)
1. 作WEB,主線程專門監聽用戶的HTTP請求,而後啓動子線程去處理用戶的HTTP請求。提升吞吐量
2.
某種任務,雖然耗時,可是不耗CPU的操做時,開啓多個線程,效率會有顯著提升。
好比讀取文件,而後處理。 磁盤IO是個很耗費時間,可是不耗CPU計算的工做。 因此能夠一個線程讀取數據,一個線程處理數據。確定比
3. 數據庫操做
產生緣由:
互斥條件:一個資源每次只能被一個進程使用。
不剝奪條件:進程已得到的資源,在末使用完以前,不能強行剝奪。
請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。
循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。
如何避免死鎖:
加鎖順序(線程按照必定的順序加鎖,只有得到了從順序上排在前面的鎖以後,才能獲取後面的鎖。與解鎖順序無關)
加鎖時限(線程嘗試獲取鎖的時候加上必定的時限,超過期限則放棄對該鎖的請求,並釋放本身佔有的鎖,而後等待一段隨機的時間再重試)
死鎖檢測
原子操做:由一組相關的操做完成,這些操做可能會操縱與其它的線程共享的資源,爲了保證獲得正確的運算結果,一個線程在執行原子操做其間,應該採起其餘的措施使得其餘的線程不能操縱共享資源。
1. sleep(long millis): 在指定的毫秒數內讓當前正在執行的線程休眠,進入阻塞狀態,不會釋放鎖
2. join():當前線程進入阻塞狀態,等待加入線程終止後才能執行。
3. setPriority(): 更改線程的優先級。
4. setName(): 爲線程設置一個名稱。
5. interrupt():中斷某個線程,這種結束方式比較粗暴,若是t線程打開了某個資源還沒來得及關閉也就是run方法尚未執行完就強制結束線程,會致使資源沒法關閉
6. wait()、Obj.wait()、Obj.notify()
必需要與synchronized(Obj)一塊兒使用,也就是wait,與notify是針對已經獲取了Obj鎖進行操做,從語法角度來講就是Obj.wait(),Obj.notify必須在synchronized(Obj){...}語句塊內。從功能上來講wait就是說線程在獲取對象鎖後,主動釋放對象鎖,同時本線程休眠。直到有其它線程調用對象的notify()喚醒該線程,才能繼續獲取對象鎖,並繼續執行。相應的notify()就是對對象鎖的喚醒操做。但有一點須要注意的是notify()調用後,並非立刻就釋放對象鎖的,而是在相應的synchronized(){}語句塊執行結束,自動釋放鎖後,JVM會在wait()對象鎖的線程中隨機選取一線程,賦予其對象鎖,喚醒線程,繼續執行。這樣就提供了在線程間同步、喚醒的操做。Thread.sleep()與Object.wait()兩者均可以暫停當前線程,釋放CPU控制權,主要的區別在於Object.wait()在釋放CPU同時,釋放了對象鎖的控制。
wait和sleep區別
sleep()睡眠時,保持對象鎖,仍然佔有該鎖;是thread的方法
而wait()睡眠時,釋放對象鎖。是object的方法
線程安全:就是說多線程訪問同一代碼,不會產生不肯定的結果。
1. synchronized同步方法
即有synchronized關鍵字修飾的方法。因爲java的每一個對象都有一個內置鎖,當用此關鍵字修飾方法時,內置鎖會保護整個方法。
也能夠修飾靜態方法,此時若是調用該靜態方法,將會鎖住整個類
2. synchronized同步代碼塊
即有synchronized關鍵字修飾的語句塊。
當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就得到了這個object的對象鎖。結果,其它線程對該object對象全部同步代碼部分的訪問都被暫時阻塞,但仍然能夠訪問該object中的非synchronized(this)同步代碼塊。
3. volatile實現線程同步
用volatile修飾的變量,線程在每次使用變量的時候,都會從主存中讀取變量最新值。變量修改後會直接改變主存內容。保證可見性,不能保證原子性
4. 使用重入鎖實現線程同步ReentrantLock
ReentrantLock 擁有Synchronized相同的併發性和內存語義,此外還多了 鎖投票,定時鎖等候和中斷鎖等候,好比能夠放棄鎖等待先作別的事情(trylock),而Synchronized不能
synchronized是在JVM層面上實現的,JVM會自動釋放鎖定,可是使用Lock則不行,lock是經過代碼實現的,要保證鎖定必定會被釋放,就必須將unLock()放到finally{}中
在資源競爭很激烈的狀況下,ReetrantLock的性能要優於Synchronized
5. 使用ThreadLocal管理變量
使用該變量的線程都得到該變量的副本,副本之間相互獨立,這樣每個線程均可以隨意修改本身的變量副本,而不會對其餘線程產生影響。
吞吐量:單位時間內成功地傳送數據的數量
1. synchronied關鍵字wait()/notify()、notifyAll()機制:
2. 條件對象的等待/通知機制:所謂的條件對象也就是配合前面咱們分析的Lock鎖對象,經過鎖對象的條件對象來實現等待/通知機制Condition conditionObj=ticketLock.newCondition()
3. 管道通訊
經過管道,將一個線程中的二進制數據消息發送給另外一個。
4.1 什麼是線程池?
線程池是一個線程集合,而後在須要執行新的任務時重用這些線程而不是新建一個線程。線程池中的每一個線程都有被分配一個任務,一旦任務已經完成了,線程回到池子中並等待下一次分配任務。
4.2 好處
第一:下降資源消耗。經過重複利用已建立的線程下降線程建立和銷燬形成的消耗。
第二:提升響應速度。當任務到達時,任務能夠不須要的等到線程建立就能當即執行。
第三:提升線程的可管理性。
4.3 適用場合?
當一個Web服務器接受到大量短小線程的請求時,使用線程池技術是很是合適的,它能夠大大減小線程的建立和銷燬次數,提升服務器的工做效率。但若是線程要求的運行時間比較長,此時線程的運行時間比建立時間要長得多,單靠減小建立時間對系統效率的提升不明顯,此時就不適合應用線程池技術,須要藉助其它的技術來提升服務器的服務效率。
4.4 建立線程池四種方式
咱們能夠經過Executors工具類的靜態方法來建立線程池。
1. newFixedThreadPool(int nThreads)
建立一個固定長度的線程池,每當提交一個任務就建立一個線程,直到達到線程池的最大數量,這時線程規模將再也不變化
2. newCachedThreadPool()
建立一個可緩存的線程池,適當狀況下可回收添加線程
3. newSingleThreadExecutor()
這是一個單線程的Executor
4. newScheduledThreadPool(int corePoolSize)
建立了一個固定長度的線程池,並且以延遲或定時的方式來執行任務,相似於Timer。
Executors 類使用 ExecutorService 提供了一個 ThreadPoolExecutor 的簡單實現,但 ThreadPoolExecutor 提供的功能遠不止這些。
ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(
corePoolSize,// 核心線程數,當提交一個任務到線程池時,線程池會建立一個線程來執行任務,即便其餘空閒的基本線程可以執行新任務也會建立線程,等到須要執行的任務數大於線程池基本大小時就再也不建立。
maximumPoolSize, // 最大線程數 ,線程池容許建立的最大線程數。若是隊列滿了,而且已建立的線程數小於最大線程數,則線程池會再建立新的線程執行任務。
keepAliveTime, // 線程活動保持時間,閒置線程存活時間 當線程空閒時間達到keepAliveTime,該線程會退出,直到線程數量等於corePoolSize。此時不會退出線程
TimeUnit.MILLISECONDS,// 時間單位,此處爲毫秒
runnableTaskQueuenew ,// 任務隊列,線程隊列用於保存執行任務的阻塞隊列
Executors.defaultThreadFactory(),// 線程工廠
RejectedExecutionHandler// 飽和策略,當隊列和線程池都滿了,說明線程池處於飽和狀態,那麼必須採起一種策略處理提交的新任務。這個策略默認狀況下是AbortPolicy,表示沒法處理新任務時拋出異常。
);
4.5 線程池的處理流程
1. 首先線程池判斷基本線程池是否已滿?沒滿,建立一個工做線程來執行任務。滿了,則進入下個流程。
2. 其次線程池判斷工做隊列是否已滿?沒滿,則將新提交的任務存儲在工做隊列裏。滿了,則進入下個流程。
3. 最後線程池判斷整個線程池是否已滿?沒滿,則建立一個新的工做線程來執行任務,滿了,則交給飽和策略來處理這個任務。
4.6 線程池組成
線程池管理器(ThreadPool):用於建立並管理線程池,包括 建立線程池,銷燬線程池,添加新任務;
工做線程(PoolWorker):線程池中線程,在沒有任務時處於等待狀態,能夠循環的執行任務;
任務隊列(taskQueue):用於存放沒有處理的任務。提供一種緩衝機制。
任務接口(Task):每一個任務必須實現的接口,以供工做線程調度任務的執行,它主要規定了任務的入口,任務執行完後的收尾工做,任務的執行狀態等;
4.7 合理的配置線程池
能夠從如下幾個角度來進行分析:
1. 任務的性質:CPU密集型任務、IO密集型任務 。
任務性質不一樣的任務能夠用不一樣規模的線程池分開處理。CPU密集型任務配置儘量少的線程數量。IO密集型任務則因爲須要等待IO操做,線程並非一直在執行任務,則配置儘量多的線程。
2. 任務的優先級:高,中和低。
優先級不一樣的任務可使用優先級隊列PriorityBlockingQueue來處理。(任務隊列裏的一種)
3. 任務的執行時間:長,中和短。
可使用優先級隊列,讓執行時間短的任務先執行。
4. 任務的依賴性:是否依賴其餘系統資源,如數據庫鏈接。
如依賴數據庫鏈接池的任務,由於線程提交SQL後須要等待數據庫返回結果,若是等待的時間越長CPU空閒時間就越長,那麼線程數應該設置越大,這樣才能更好的利用CPU。
public class MyThreadPrinter2 implements Runnable {
private String name;
private Object prev;
private Object self;
private MyThreadPrinter2(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);
new Thread(pa).start();
Thread.sleep(100); //確保按順序A、B、C執行
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
Thread.sleep(100);
}
}
主要的思想就是,爲了控制執行的順序,必需要先持有prev鎖,也就前一個線程要釋放自身對象鎖,再去申請自身對象鎖,二者兼備時打印,以後首先調用self.notify()釋放自身對象鎖,喚醒下一個等待線程,再調用prev.wait()釋放prev對象鎖,等待下次獲取prev鎖後運行,終止當前線程,等待循環結束後再次被喚醒。
特徵:
單例類只能有一個實例。
單例類必須本身建立本身的惟一實例。
單例類必須給全部其餘對象提供這一實例。
懶漢式單例:
Public class Singleton {
Private Singleton (){};
Private static Singleton single = null;
Public static Singleton getInstance(){
If(Singleton == null)
Single = new Singleton ();
return single;
}
}
可是以上懶漢式單例的實現沒有考慮線程安全問題,它是線程不安全的,併發環境下極可能出現多個Singleton實例,要實現線程安全,有如下三種方式
方法一:在getInstance方法上加同步
public static synchronized Singleton getInstance(){
If(Single == null)
Single = new Singleton ();
return single;
}
方法二 : 雙檢索
public static Singleton getInstance(){
If(single == null){
Synchronized(Singleton.class){
if(single == null)
Single = new Singleton ();
}
}
return single;
}
方法三:靜態內部類
餓漢式單例:
Public class singleton{
Private singleton(){}
Private static final singleton single = new singleton();
Public static singleton getInstance(){
return single;
}
}
單生產者單消費者模式:
1. public class KaoYaResource {
2.
3. private String name;
4. private int count = 1;//烤鴨的初始數量
5. private boolean flag = false;//判斷是否有須要線程等待的標誌
6. public synchronized void product(String name){
7. if(flag){
8. //此時有烤鴨,等待
9. try {
10. this.wait();
11. } catch (InterruptedException e) {
12. e.printStackTrace();
13. }
14. }
15. this.name=name+count;//設置烤鴨的名稱
16. count++;
17. System.out.println(Thread.currentThread().getName()+"...生產者..."+this.name);
18. flag=true;//有烤鴨後改變標誌
19. notifyAll();//通知消費線程能夠消費了
20. }
21. public synchronized void consume(){
22. if(!flag){//若是沒有烤鴨就等待
23. try{this.wait();}catch(InterruptedException e){}
24. }
25. System.out.println(Thread.currentThread().getName()+"...消費者........"+this.name);//消費烤鴨1
26. flag = false;
27. notifyAll();//通知生產者生產烤鴨
28. }
29. }
30.
1. public class Single_Producer_Consumer {
2. public static void main(String[] args)
3. {
4. KaoYaResource r = new KaoYaResource();
5. Producer pro = new Producer(r);
6. Consumer con = new Consumer(r);
7. //生產者線程
8. Thread t0 = new Thread(pro);
9. //消費者線程
10. Thread t2 = new Thread(con);
11. //啓動線程
12. t0.start();
13. t2.start();
14. }
15. }
16. class Producer implements Runnable
17. {
18. private KaoYaResource r;
19. Producer(KaoYaResource r)
20. {
21. this.r = r;
22. }
23. public void run()
24. {
25. while(true)
26. {
27. r.product("北京烤鴨");
28. }
29. }
30. }
31. class Consumer implements Runnable
32. {
33. private KaoYaResource r;
34. Consumer(KaoYaResource r)
35. {
36. this.r = r;
37. }
38. public void run()
39. {
40. while(true)
41. {
42. r.consume();
43. }
44. }
}