好程式設計師Java培訓分享Java多執行緒併發

好程式設計師發表於2020-07-14

  好程式設計師Java 培訓分享 Java 多執行緒併發, 1 Java 執行緒實現 / 建立方式

  繼承 Thread

  Thread 類本質上是實現了 Runnable 介面的一個例項,代表一個執行緒的例項。啟動執行緒的唯一方法就是透過 Thread 類的 start() 例項方法。 start() 方法是一個 native 方法,它將啟動一個新執行緒,並執行 run() 方法。

 

public class MyThread extends Thread {

 

public void run() {

 

System.out.println("MyThread.run()");

 

} }

 

MyThread myThread1 = new MyThread();

 

myThread1.start();

 

實現 Runnable 介面

 

如果自己的類已經 extends 另一個類,就無法直接 extends Thread ,此時,可以實現一個 Runnable 介面。

 

public class MyThread extends OtherClass implements Runnable {

 

public void run() {

 

System.out.println("MyThread.run()");

 

} } // 啟動

 

MyThreadMyThread myThread = new MyThread();

 

Thread thread = new Thread(myThread);

 

thread.start();

 

target.run()public void run() {

 

if (target != null) {

 

target.run();

 

} }

 

ExecutorService Callable Future 有返回值執行緒

 

有返回值的任務必須實現 Callable 介面,類似的,無返回值的任務必須 Runnable 介面。執行 Callable 任務後,可以獲取一個 Future 的物件,在該物件上呼叫 get 就可以獲取到 Callable 任務返回的 Object 了,再結合執行緒池介面 ExecutorService 就可以實現傳說中有返回結果的多執行緒了。

 

// 建立一個執行緒池

 

ExecutorService pool = Executors.newFixedThreadPool(taskSize);

 

// 建立多個有返回值的任務

 

List<Future> list = new ArrayList<Future>();

 

for (int i = 0; i < taskSize; i++) {

 

Callable c = new MyCallable(i + " ");

 

// 執行任務並獲取 Future 物件

 

Future f = pool.submit(c); list.add(f);

 

} // 關閉執行緒池 pool.shutdown();

 

// 獲取所有併發任務的執行結果 for (Future f : list) {

 

// Future 物件上獲取任務的返回值,並輸出到控制檯 System.out.println("res " + f.get().toString());

 

}

 

基於執行緒池的方式

 

執行緒和資料庫連線這些資源都是非常寶貴的資源。如果每次需要的時候建立,不需要的時候銷燬,是非常浪費資源的。那麼我們就可以使用快取的策略,也就是使用執行緒池。

 

// 建立執行緒池 ExecutorService threadPool = Executors.newFixedThreadPool(10);

 

while(true) { threadPool.execute(new Runnable() {

 

// 提交多個執行緒執行 @Override public void run() {

 

System.out.println(Thread.currentThread().getName() + " is running ..");

 

try {

 

Thread.sleep(3000);

 

}

 

catch (InterruptedException e) {

 

e.printStackTrace();

 

} } }); }}

 

2 同步鎖與死鎖

 

同步鎖當多個執行緒同時訪問同一個資料時,很容易出現問題。為了避免這種情況出現,我們要保證執行緒同步互斥,就是指併發執行的多個執行緒,在同一時間內只允許一個執行緒訪問共享資料。Java 中可以使用 synchronized 關鍵字來取得一個物件的同步鎖。

 

死鎖何為死鎖,就是多個執行緒同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。

 

3 執行緒池原理

 

執行緒池做的工作主要是控制執行的執行緒的數量,處理過程中將任務放入佇列,然後線上程建立後啟動這些任務,如果執行緒數量超過了最大數量超出數量的執行緒排隊等候,等其它執行緒執行完畢,再從佇列中取出任務來執行。主要特點為:執行緒複用; 控制最大併發數 ; 管理執行緒。

 

執行緒複用一個Thread 的類都有一個 start 方法。當呼叫 start 啟動執行緒時 Java 虛擬機器會呼叫該類的 run 方法。那麼該類的 run() 方法中就是呼叫了 Runnable 物件的 run() 方法。我們可以繼承重寫 Thread 類,在其 start 方法中新增不斷迴圈呼叫傳遞過來的 Runnable 物件。這就是執行緒池的實現原理。迴圈方法中不斷獲取 Runnable 是用 Queue 實現的,在獲取下一個 Runnable 之前可以是阻塞的。

 

執行緒池的組成一般的執行緒池主要分為以下 4 個組成部分:

 

(1) 執行緒池管理器:用於建立並管理執行緒池。 (2) 工作執行緒:執行緒池中的執行緒。 (3) 任務介面:每個任務必須實現的介面,用於工作執行緒排程其執行。 (4) 任務佇列:用於存放待處理的任務,提供一種緩衝機制。

 

Java 中的執行緒池是透過 Executor 框架實現的,該框架中用到了 Executor Executors ExecutorService ThreadPoolExecutor Callable Future FutureTask 這幾個類。

 

ThreadPoolExecutor 的構造方法如下:

 

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);}

 

corePoolSize :指定了執行緒池中的執行緒數量。

 

maximumPoolSize :指定了執行緒池中的最大執行緒數量。

 

keepAliveTime :當前執行緒池數量超過 corePoolSize 時,多餘的空閒執行緒的存活時間,即多次時間內會被銷燬。

 

unit keepAliveTime 的單位。

 

workQueue :任務佇列,被提交但尚未被執行的任務。

 

threadFactory :執行緒工廠,用於建立執行緒,一般用預設的即可。

 

handler :拒絕策略,當任務太多來不及處理,如何拒絕任務。

 

拒絕策略執行緒池中的執行緒已經用完了,無法繼續為新任務服務,同時,等待佇列也已經排滿了,再也塞不下新任務了。這時候我們就需要拒絕策略機制合理的處理這個問題。

 

JDK 內建的拒絕策略如下:

 

AbortPolicy :直接丟擲異常,阻止系統正常執行。

 

CallerRunsPolicy :只要執行緒池未關閉,該策略直接在呼叫者執行緒中,執行當前被丟棄的任務。顯然這樣做不會真的丟棄任務,但是,任務提交執行緒的效能極有可能會急劇下降。

 

DiscardOldestPolicy :丟棄最老的一個請求,也就是即將被執行的一個任務,並嘗試再次提交當前任務。

 

DiscardPolicy :該策略默默地丟棄無法處理的任務,不予任何處理。如果允許任務丟失,這是最好的一種方案。

 

以上內建拒絕策略均實現了 RejectedExecutionHandler 介面,若以上策略仍無法滿足實際需要,完全可以自己擴充套件 RejectedExecutionHandler 介面。

 

Java 執行緒池工作過程 (1) 執行緒池剛建立時,裡面沒有一個執行緒。任務佇列是作為引數傳進來的。不過,就算佇列裡面有任務,執行緒池也不會馬上執行它們。

 

(2) 當呼叫 execute() 方法新增一個任務時,執行緒池會做如下判斷:

 

a) 如果正在執行的執行緒數量小於 corePoolSize ,那麼馬上建立執行緒執行這個任務 ;

 

b) 如果正在執行的執行緒數量大於或等於 corePoolSize ,那麼將這個任務放入佇列 ;

 

c) 如果這時候佇列滿了,而且正在執行的執行緒數量小 maximumPoolSize ,那麼還是要建立非核心執行緒立刻執行這個任務 ;

 

d) 如果佇列滿了,而且正在執行的執行緒數量大於或等 maximumPoolSize ,那麼執行緒池會丟擲異常 RejectExecutionException

 

(3) 當一個執行緒完成任務時,它會從佇列中取下一個任務來執行。

 

(4) 當一個執行緒無事可做,超過一定的時間 (keepAliveTime) 時,執行緒池會判斷,如果當前執行的執行緒數大於 corePoolSize ,那麼這個執行緒就被停掉。所以執行緒池的所有任務完成後,它最終會收縮到 corePoolSize 的大小。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69913864/viewspace-2704333/,如需轉載,請註明出處,否則將追究法律責任。

相關文章