java多線程面試總結

一:基本知識點

1.1線程與進程區別:

1. 進程是資源分配的最小單位,線程是CPU調度的最小單位java

2. 一個進程由一個或多個線程組成mysql

3. 進程之間相互獨立,每一個進程都有獨立的代碼和數據空間,但同一進程下的各個線程之間共享進程的代碼和內存空間,每一個線程有獨立的運行棧和程序計數器sql

4. 線程上下文切換比進程上下文切換要快得多數據庫

1.2線程實現

java中要想實現多線程,有兩種手段,一種是繼續Thread類(extends )緩存

另一種是實現Runable接口(implements ,須要先經過Thread類的構造方法Thread(Runnable target) 構造出對象)。安全

實現runnable接口的優點:服務器

適合於資源的共享多線程

能夠避免java中的單繼承的限制併發

增長程序的健壯性,代碼能夠被多個線程共享ide

1.3線程狀態轉換

新建狀態(New):新建立了一個線程對象。

就緒狀態(Runnable):線程對象建立後,其餘線程調用了該對象的start()方法。變得可運行,等待獲取CPU的使用權。

運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。

阻塞狀態(Blocked):阻塞狀態是線程由於某種緣由放棄CPU使用權,暫時中止運行。

死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

1.4多線程應用場景(是爲了充分利用cpu)

1 線程間有數據共享,而且數據是須要修改的(不一樣任務間須要大量共享數據或頻繁通訊時); 
2 提供非均質的服務(有優先級任務處理)事件響應有優先級; 
3 單任務並行計算,提升響應速度,下降時延; 
4 與人有IO交互的應用,良好的用戶體驗(鍵盤鼠標的輸入,馬上響應)

1.  WEB,主線程專門監聽用戶的HTTP請求,而後啓動子線程去處理用戶的HTTP請求。提升吞吐量

2.  某種任務,雖然耗時,可是不耗CPU的操做時,開啓多個線程,效率會有顯著提升。
好比讀取文件,而後處理。 磁盤IO是個很耗費時間,可是不耗CPU計算的工做。 因此能夠一個線程讀取數據,一個線程處理數據。確定比

3.  數據庫操做

1.5死鎖

產生緣由:

互斥條件:一個資源每次只能被一個進程使用。

不剝奪條件:進程已得到的資源,在末使用完以前,不能強行剝奪。

請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。

循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。

如何避免死鎖:

加鎖順序(線程按照必定的順序加鎖只有得到了從順序上排在前面的鎖以後,才能獲取後面的鎖。與解鎖順序無關

加鎖時限(線程嘗試獲取鎖的時候加上必定的時限,超過期限則放棄對該鎖的請求,並釋放本身佔有的鎖而後等待一段隨機的時間再重試)

死鎖檢測

原子操做:由一組相關的操做完成,這些操做可能會操縱與其它的線程共享的資源,爲了保證獲得正確的運算結果,一個線程在執行原子操做其間,應該採起其餘的措施使得其餘的線程不能操縱共享資源。

1.6經常使用函數

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同時,釋放了對象鎖的控制。

waitsleep區別 

sleep()睡眠時,保持對象鎖,仍然佔有該鎖;thread的方法
  而wait()睡眠時,釋放對象鎖。object的方法

 

2、 線程同步五種方式

線程安全:就是說多線程訪問同一代碼,不會產生不肯定的結果。

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管理變量

使用該變量的線程都得到該變量的副本,副本之間相互獨立,這樣每個線程均可以隨意修改本身的變量副本,而不會對其餘線程產生影響。

 

吞吐量:單位時間內成功地傳送數據的數量

3、線程間通訊

1. synchronied關鍵字wait()/notify()、notifyAll()機制:

2. 條件對象的等待/通知機制(await()、signal()、signalAll()):所謂的條件對象也就是配合前面咱們分析的Lock鎖對象,經過鎖對象的條件對象來實現等待/通知機制Condition conditionObj=ticketLock.newCondition()

3. 管道通訊

經過管道,將一個線程中的二進制數據消息發送給另外一個。

 

進程間通訊:

 

1.管道

 

  匿名管道:管道是一種半雙工的通訊方式,數據只能單向流動,並且只能在具備親緣關係(父子)的進程間使用。

 

  有名管道: 有名管道也是半雙工的通訊方式,可是它容許無親緣關係進程間的通訊。

 

2.信號量: 信號量是一個計數器,能夠用來控制多個進程對共享資源的訪問。

 

3.消息隊列: 消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流

 

4.信號:用於通知接收進程某個事件已經發生。

 

5 .共享內存:共享內存是最快的一種 IPC,由於進程是直接對內存進行存取。共享內存就是一段能被其餘進程所訪問的內存,這段共享內存由一個進程建立,但多個進程均可以訪問信號量+共享內存一般結合在一塊兒使用,來達到進程間的同步與互斥。

 

4、線程池

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

5、同步案例

5.1 順序打印ABCwait()notify()

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);  //確保按順序ABC執行

        new Thread(pb).start();

        Thread.sleep(100);  

        new Thread(pc).start();   

        Thread.sleep(100);  

        }   

}  

主要的思想就是,爲了控制執行的順序,必需要先持有prev鎖,也就前一個線程要釋放自身對象鎖,再去申請自身對象鎖,二者兼備時打印,以後首先調用self.notify()釋放自身對象鎖,喚醒下一個等待線程,再調用prev.wait()釋放prev對象鎖,等待下次獲取prev鎖後運行,終止當前線程,等待循環結束後再次被喚醒。

 

5.2 單例模式

特徵:

單例類只能有一個實例。

單例類必須本身建立本身的惟一實例。

單例類必須給全部其餘對象提供這一實例。

懶漢式單例

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;

}

}

5.3 生產者消費者模式

單生產者單消費者模式:

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.1線程與進程區別:

1. 進程是資源分配的最小單位,線程是CPU調度的最小單位

2. 一個進程由一個或多個線程組成

3. 進程之間相互獨立,每一個進程都有獨立的代碼和數據空間,但同一進程下的各個線程之間共享進程的代碼和內存空間,每一個線程有獨立的運行棧和程序計數器

4. 線程上下文切換比進程上下文切換要快得多

1.2線程實現

java中要想實現多線程,有兩種手段,一種是繼續Thread類(extends )

另一種是實現Runable接口(implements ,須要先經過Thread類的構造方法Thread(Runnable target) 構造出對象)。

實現runnable接口的優點:

適合於資源的共享

能夠避免java中的單繼承的限制

增長程序的健壯性,代碼能夠被多個線程共享

1.3線程狀態轉換

新建狀態(New):新建立了一個線程對象。

就緒狀態(Runnable):線程對象建立後,其餘線程調用了該對象的start()方法。變得可運行,等待獲取CPU的使用權。

運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。

阻塞狀態(Blocked):阻塞狀態是線程由於某種緣由放棄CPU使用權,暫時中止運行。

死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

1.4多線程應用場景(是爲了充分利用cpu)

1 線程間有數據共享,而且數據是須要修改的(不一樣任務間須要大量共享數據或頻繁通訊時); 
2 提供非均質的服務(有優先級任務處理)事件響應有優先級; 
3 單任務並行計算,提升響應速度,下降時延; 
4 與人有IO交互的應用,良好的用戶體驗(鍵盤鼠標的輸入,馬上響應)

1.  WEB,主線程專門監聽用戶的HTTP請求,而後啓動子線程去處理用戶的HTTP請求。提升吞吐量

2.  某種任務,雖然耗時,可是不耗CPU的操做時,開啓多個線程,效率會有顯著提升。
好比讀取文件,而後處理。 磁盤IO是個很耗費時間,可是不耗CPU計算的工做。 因此能夠一個線程讀取數據,一個線程處理數據。確定比

3.  數據庫操做

1.5死鎖

產生緣由:

互斥條件:一個資源每次只能被一個進程使用。

不剝奪條件:進程已得到的資源,在末使用完以前,不能強行剝奪。

請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。

循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。

如何避免死鎖:

加鎖順序(線程按照必定的順序加鎖只有得到了從順序上排在前面的鎖以後,才能獲取後面的鎖。與解鎖順序無關

加鎖時限(線程嘗試獲取鎖的時候加上必定的時限,超過期限則放棄對該鎖的請求,並釋放本身佔有的鎖而後等待一段隨機的時間再重試)

死鎖檢測

原子操做:由一組相關的操做完成,這些操做可能會操縱與其它的線程共享的資源,爲了保證獲得正確的運算結果,一個線程在執行原子操做其間,應該採起其餘的措施使得其餘的線程不能操縱共享資源。

1.6經常使用函數

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同時,釋放了對象鎖的控制。

waitsleep區別 

sleep()睡眠時,保持對象鎖,仍然佔有該鎖;thread的方法
  而wait()睡眠時,釋放對象鎖。object的方法

 

2、 線程同步五種方式

線程安全:就是說多線程訪問同一代碼,不會產生不肯定的結果。

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管理變量

使用該變量的線程都得到該變量的副本,副本之間相互獨立,這樣每個線程均可以隨意修改本身的變量副本,而不會對其餘線程產生影響。

 

吞吐量:單位時間內成功地傳送數據的數量

3、線程間通訊

1. synchronied關鍵字wait()/notify()、notifyAll()機制:

2. 條件對象的等待/通知機制:所謂的條件對象也就是配合前面咱們分析的Lock鎖對象,經過鎖對象的條件對象來實現等待/通知機制Condition conditionObj=ticketLock.newCondition()

 

3. 管道通訊

經過管道,將一個線程中的二進制數據消息發送給另外一個。

4、線程池

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

5、同步案例

5.1 順序打印ABCwait()notify()

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);  //確保按順序ABC執行

        new Thread(pb).start();

        Thread.sleep(100);  

        new Thread(pc).start();   

        Thread.sleep(100);  

        }   

}  

主要的思想就是,爲了控制執行的順序,必需要先持有prev鎖,也就前一個線程要釋放自身對象鎖,再去申請自身對象鎖,二者兼備時打印,以後首先調用self.notify()釋放自身對象鎖,喚醒下一個等待線程,再調用prev.wait()釋放prev對象鎖,等待下次獲取prev鎖後運行,終止當前線程,等待循環結束後再次被喚醒。

 

5.2 單例模式

特徵:

單例類只能有一個實例。

單例類必須本身建立本身的惟一實例。

單例類必須給全部其餘對象提供這一實例。

懶漢式單例

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;

}

}

5.3 生產者消費者模式

單生產者單消費者模式:

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.      }  

}  

相關文章
相關標籤/搜索