---
# System prepended metadata

title: 執行緒 Thread
tags: [知識筆記]

---

# 執行緒Thread
## 簡介
想像一下售票網站同時間有十萬人湧入要買票，
我們如果只有單一執行緒處理(一個售票窗口)，肯定卡到炸裂、客訴到升天。
所以為了讓消化速度更快，我們會使用多執行緒(多個售票窗口)來加速處理。
但，如果此時最後一張票在A窗口賣完了，
但B、C、D窗口不知道已售罄，還在賣票，就會超賣。
在執行緒的世界裡，這種情況我們要使用同步synchronized。

另外Thread又可分為main Thread與users Thread與Daemon Thread。
main Thread就是我們一般所謂的main方法，而在main裡面呼叫的其他thread，都屬於main的子thread。

在所有users Thread執行完畢前，JVM都不會終止(關閉)。
而Daemon Thread又有點像背景執行的程式，如果其他users Thread都已完成任務並終止，那無論Daemon Thread是否尚未結束任務，都會被終止。
所以在設計時需考慮是否需要Daemon Thread的存在，且Daemon Thread只是用來輔助users Thread的設計。

最後特別注意的是：「**在一般狀況下，不要自己預設執行緒的先後順序會如何進行。**」，因為它是交給JVM去決定的。
## 使用Thread的兩種方式
### 繼承Thread
範例:
```java=
class 類別名稱 extends Thread
{
   資料成員;
   函數;
   run()
   {
     //執行緒
   }
}
```
也就是
```java=
class multi_Test extends Thread
{
   private String name;
   public CTest(String str)
   {
      name=str;
   }
   public void run()
   {
      for(int i=0;i<3;i++)
      {
         for(int j=0;j<100000000;j++);
         System.out.println(name);
      }
   }
}
```
在main() 中：使用 start來呼叫。
```java=
multi_Test apple=new multi_Test("apple");
multi_Test pen=new multi_Test("pen");
apple.start();
pen.start();
```
為什麼使用start() ？
因為如果直接呼叫 run() ，這樣並無法啟動執行緒，只是再將執行緒執行一遍而已。
### 實作Runnable 介面
Runnable 介面，有抽象函數 run() ；因此我們只需要定義 run() 即可。
```JAVA=
class multi_Test implements Runnable
{
   private String name;
   public multi_Test(String str)
   {
      name=str;
   }
   public void run()
   {
      for(int i=0;i<3;i++)
      {
         for(int j=0;j<100000000;j++);
         System.out.println(id+" is running..");
      }
   }
}

public class multi_thread_3
{
   public static void main(String args[])
   {
      multi_Test apple=new multi_Test("apple");
      multi_Test pen=new multi_Test("pen");
      Thread m1=new Thread(apple);
      Thread m2=new Thread(pen);
      m1.start();
      m2.start();
   }
}

```
在main() 中：我們建立物件後，再分別產生 Thread 類別的物件 來呼叫 start() 啟動執行緒
### 兩者差異
繼承 Thread class的方式和實作 Runnable interface 的主要差別如下：
1. 使用繼承extends Thread 的方式，之後就不能繼承其它的 class，
但是使用實作implements Runnable 的方式，之後還是可以實作其它 Interface 或是繼承其它有需要的 class。
2. 使用繼承 Thread 的方式直接 new Thread 繼承的 class 就可以 start。
但是使用實作 Runnable 的方式需要再 new 一個 Thread 的 class，再把實作的 Runnable 傳入 Thread 的建構子裡面，才能呼叫 start 方法。
3. 在使用共享資源方面使用 Runnable 的 interface 較為方便。
## 執行緒關係圖
![執行緒](https://hackmd.io/_uploads/rkEIIl4OT.png)
### 執行緒生命週期五個狀態
1. 新產生的 newly created
使用new Thread() 建立物件時，就會進入此狀態。（此時系統不會配置資源）
2. 可執行的 runable
當 start() 啟動執行緒，最先搶到資源的先執行 run()；未搶到的將會在 queue 佇列中等待爭取。
3. 執行中的 running
先搶到資源的執行緒就會進入這個狀態。（一次僅有一個執行緒會在此狀態）
4. 被凍結的 blocked
碰到以下三點時，就會進入凍結狀態。
呼叫 wait()
呼叫 sleep()
sleep() 函數，可用來設定睡眠時間，即 sleep(long millis)。
毫秒（千分之一秒）。
與另一個執行緒 join() 一起時：當有其他執行緒呼叫 join()，原來正執行的執行緒（或程式碼）會先暫停
那麼當執行緒被凍結，就會停止 run() 的執行，等到凍結的因素消失就會回到可執行狀態（排隊搶資源）。
以下是凍結消失的因素：
若執行緒是因為呼叫物件 wait() 而凍結，當物件的 notify() 被呼叫時就可以解凍結。
進入睡眠 sleep 狀態，而指定時間已到。
5. 銷毀的 dead
當 run() 結束，或呼叫 stop() 時就會進入此狀態。（進入此狀態就無法再次啟動）
## 執行緒的順序
在前篇使用了 join() 來控制安排執行緒。那麼執行緒一多，我們可以安排哪些執行緒優先順序是較高的。
Java 執行緒的優先順序是使用 1~10來表示，**數字大代表優先權較高**（意指先執行）。
有兩個函數與優先權相關：
`setPriority()` 用來設定優先順序，可以使用以下三個常用的代碼：
MAX_PRIORTY 最大優先權數值
MIN_PRIORTY 最小優先權數值
NORM_PRIORTY 系統預設

`getPriority()` 用來取得順序之值
```JAVA=
class multi_Test
{
   private String name;
   public multi_Test(String str)
   {
      name=str;
   }
   public void show()
   {
      for(int i=0;i<3;i++)
      {
         try
         {
            sleep((int)(1000*Math.random()));
         }
         catch(InterruptedException e){}
         System.out.println(name+" 執行中"+this.getPriority());
      }
   }
}

public class multi_thread_3
{
public static void main(String args[])
{
   multi_Test apple=new multi_Test("red");
   multi_Test banana=new multi_Test("yellow");
   multi_Test grape=new multi_Test("purple");
   multi_Test cloud =new multi_Test("white");
   multi_Test grass=new multi_Test("green");

   apple.setPriority(Thread.MAX_PRIORITY);
   banana.setPriority(Thread.MIN_PRIORITY);
   cloud.setPriority(3);
   grass.setPriority(6);

   apple.start();
   banana.start();
   grape.start();
   cloud.start();
   grass.start();
   }
}
```
### join()
join() 是 Java Thread 類中的一個方法，用於執行緒協作。
它允許一個執行緒等待另一個執行緒的完成。
通過調用 join() 方法，當前執行緒可以掛起執行，直到目標執行緒終止或等待超時，
這使得多執行緒編程中的協調和同步變得更容易。
#### join() 的作用
當一個執行緒 A 調用執行緒 B 的 join() 方法時，執行緒 A 將進入等待狀態，直到執行緒 B 執行完畢（即 B 的 run() 方法結束）。這意味著執行緒 A 在 B 結束之前不會繼續執行後續的代碼。這在需要確保某些任務在其他任務完成後再執行的情況下非常有用。
#### join() 的使用場景
1. 確保任務順序執行:
在某些情況下，你可能需要確保一個執行緒在另一個執行緒完成後再繼續執行。例如，你希望所有的子執行緒完成後，主執行緒再繼續執行。
2. 同步多個執行緒:
在多執行緒程序中，如果有多個執行緒需要同步完成某些任務，join() 方法可以確保執行緒之間的執行順序和依賴關係。
### yield()
yield() 是 Java 中 Thread 類的一個靜態方法，用來提示調度器當前執行緒願意讓出 CPU 使用權，允許其他相同或更高優先級的執行緒獲得執行機會。
然而，yield() 只是給調度器一個提示，並不保證當前執行緒會立即停止執行。

#### yield() 的作用和行為
1. 讓出 CPU:
當執行緒調用 yield() 時，它會將自己從運行狀態轉變為可運行狀態（runnable），這意味著它願意讓出 CPU 給其他執行緒使用。這樣一來，操作系統的執行緒調度器可以選擇將 CPU 分配給其他處於可運行狀態的執行緒。
2. 不一定會切換:
yield() 只是建議調度器切換執行緒，但不保證會立即發生執行緒切換。如果沒有其他同優先級或更高優先級的執行緒在等待，調度器可能會繼續讓當前執行緒執行。
因此，yield() 的實際效果在不同的平台和 JVM 實現中可能有所不同。
3. 不會釋放鎖:
yield() 不會釋放執行緒持有的任何鎖。
因此，如果一個執行緒持有鎖並調用了 yield()，其他需要該鎖的執行緒仍然無法繼續執行。
#### yield() 的使用場景
1. 調試和測試:
yield() 通常用於調試和測試目的，或者在某些情況下，通過引入讓出來改善系統的公平性。但由於其行為並不保證，因此不應依賴 yield() 來實現精確的同步或多執行緒調度。
2. 降低資源消耗:
在某些情況下，可以使用 yield() 讓出 CPU 來降低資源的消耗，從而為其他執行緒提供機會。這在高優先級執行緒希望避免長時間占用 CPU 時有一定的作用。
## 同步處理 Synchronized
synchronized 有「同步」的意思，在Java中這個**關鍵字**可以使各執行緒在時間上做協調，也就是「**一次只允許一個執行緒進行處理**」，而其他的執行緒必須等待上個執行緒處理完後才可以進入處理。
當數個執行緒同時啟動，而且還共用同個變數，就會常發生無法發覺的錯誤。
可以有兩種方式去達到這個目的：
1. Synchronized方法
在方法前面加上Synchronized
```java=
public synchronized void sample(){
    
}
```
對象鎖 (Instance Lock): 對於實例方法，synchronized 使用的是當前對象 (this) 作為鎖
類鎖 (Class Lock): 對於靜態方法，synchronized 使用的是當前類的 Class 對象作為鎖。

2. Synchronized區塊
在方法中加入Synchronized同步區塊與傳入鎖的類型
```java=
public void sample(){
    synchronized(鎖的類型,例如物件或this){
        
    }
}
```

### synchronized 的特性
1. 可重入性 (Reentrant): 
如果一個執行緒已經擁有了一個 synchronized 鎖，它可以再次進入該鎖保護的代碼區域，而不會被阻塞。

2. 可見性 (Visibility): 
synchronized 確保了鎖保護的變數的可見性，當一個執行緒修改了變數，其他執行緒可以看到最新的值。

3.原子性 (Atomicity): 
synchronized 確保了對象的同步方法或代碼塊的原子性，這意味著操作不會被中斷。

**需要注意的地方**
1.性能開銷: 
使用 synchronized 會帶來一定的性能開銷，因為它涉及到上下文切換和鎖的管理。因此，應該只在需要的地方使用同步。

2.死鎖 (Deadlock): 
如果不小心設計，可能會導致死鎖情況，兩個或多個執行緒彼此等待對方釋放鎖，從而無法繼續執行。
## volatile關鍵字
volatile是JAVA中的一個關鍵字，用來修飾變數，像是：
```java=
private volatile int number = 1; 
```
表示該變數的值在多個執行緒之間可見，並且禁止對其進行指令重排序。
它主要用於解決多執行緒環境下變數的可見性問題。

1. 保證變數的可見性:
在多執行緒環境下，當一個執行緒修改了一個 volatile 變數的值時，這個改動對其他執行緒立即可見。這意味著，當一個執行緒更新了 volatile 變數的值，其他執行緒將能夠立即看到這個更新。
通常，Java 的變數存取可能會出現可見性問題，因為每個執行緒都有自己的高速Cache，如果變數沒有被設為 volatile，那麼某個執行緒的改動可能不會立即被其他執行緒看到。
**volatile確保了變數的值會直接從主記憶體讀取和寫入，而不是從執行緒的本地Cache中讀取。**

2. 防止指令重排序:
volatile 禁止編譯器和 CPU 對該變數進行指令重排序操作，確保程序的執行順序按照代碼中的邏輯順序執行。這在某些需要保持執行順序的場景中特別重要，比如雙重檢查鎖定模式中的單例實現。

#### 注意事項
1. 無法保證操作的原子性:
volatile 僅僅保證可見性，不能保證對變數操作的原子性。比如對於 volatile int 的自增操作（i++），雖然可以保證每次讀取到的值是最新的，但操作本身不是原子性的，可能會出現執行緒安全問題。在這種情況下，需要使用 synchronized 或 AtomicInteger 等工具來保證原子性。

2. 適用範圍有限:
volatile 適合用於簡單的狀態標記或讀取-寫入操作，它不適合用於複雜的執行緒協作邏輯。在這些情況下，synchronized 或其他併發工具（如 Lock、Semaphore 等）可能會更合適。

總結來說，volatile 是一個輕量級的同步工具，主要用來解決多執行緒中的可見性問題，並且對性能的影響較小。然而，對於需要複雜同步邏輯的場景，應該考慮更強大的併發控制工具。

## AtomicInteger
AtomicInteger 是 Java 中位於 `java.util.concurrent.atomic` 套件中的一個類別，專門用來進行整數類型的原子操作。所謂「原子操作」，指的是這些操作在多執行緒環境下是不可分割的，即使多個執行緒同時修改同一個 AtomicInteger 變數，也不會產生數據競爭 (data race) 或不一致的問題。

程式範例:
```java=
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerExample {
    private static final AtomicInteger counter = new AtomicInteger(0);

    public static void main(String[] args) {
        // 建立1000個執行緒，每個執行緒都將計數器加1
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                System.out.println(counter.incrementAndGet());
            }).start();
        }
    }
}

```


## 執行緒池ThreadPool
ThreadPool執行緒池（線程池）是一種管理多執行緒執行任務的設計模式，主要目的是**控制併發執行的執行緒數量**，以達到提升性能和有效資源利用的目的。在 Java 中，ThreadPool 通常是透過 `java.util.concurrent.ExecutorService` 介面和其實現類來使用的，例如:ThreadPoolExecutor。

### 特性
1. 資源管理
線程池允許我們重複使用已創建的執行緒，減少創建和銷毀執行緒的開銷。
通過限制同時執行的執行緒數量，線程池避免了系統資源的過度消耗。
2. 任務管理
線程池能夠接收、排隊和執行任務，當一個任務完成後，執行緒可以被重新利用來處理新的任務。
可以設置不同的策略來處理排隊任務，例如丟棄新任務、丟棄最早的任務、直接執行等。
3. 併發控制
線程池提供了併發控制的能力，例如控制最大同時執行的執行緒數量，從而避免系統過載。

## countdownlatch
CountDownLatch 是 Java 中位於 `java.util.concurrent` 套件中的一個同步輔助類別。它允許一個或多個執行緒等待，直到其他執行緒完成一組操作後，再繼續執行。CountDownLatch 是一種計數器機制，用來管理多執行緒之間的協作。
## BlockingQueue
Java 中一個位於 `java.util.concurrent` 套件中的介面，用於在多執行緒環境下安全地進行隊列操作。它是佇列的一種變體，支持生產者-消費者模式，其中生產者執行緒向隊列中添加元素，消費者執行緒從隊列中取出元素。BlockingQueue 提供了在操作不能立即完成時自動阻塞執行緒的功能，從而避免忙等 (busy waiting)。

## ReentrantLock可重入鎖
`Java java.util.concurrent.locks` 包中提供的一個鎖實現。它是一種可重入的鎖（reentrant lock），允許執行緒多次獲取同一個鎖，而不會被自己阻塞。相比於 synchronized 關鍵字，ReentrantLock 提供了更多的功能和控制。
### 特性
1. 可重入性（Reentrancy）：
ReentrantLock 是可重入的，這意味著如果一個執行緒已經持有了這個鎖，它可以再次獲取該鎖而不會被阻塞。鎖的計數器會記錄鎖的獲取次數，每次獲取鎖時計數器增加，釋放鎖時計數器減少，當計數器為 0 時，鎖實際被釋放。
2. 公平鎖（Fair Lock）：
ReentrantLock 可以選擇性地設置為公平鎖。公平鎖的特點是，它按先來先服務的順序分配鎖，這樣可以避免飢餓問題。非公平鎖則可能讓某些執行緒獲得更多的鎖定機會（可能會更高效），但可能導致某些執行緒長期等待。
3. 手動鎖定和釋放：
使用 ReentrantLock，需要手動鎖定和釋放鎖，這提供了比 synchronized 更靈活的鎖定方式，但同時也要求開發者小心管理鎖的釋放，避免死鎖。
4. tryLock() 和 lockInterruptibly()：
**tryLock()**：這個方法會嘗試獲取鎖，若鎖未被獲取，它會立即返回 false 而不會等待。這對於避免死鎖很有用。
**lockInterruptibly()**：這個方法允許鎖在等待時被中斷。
5. Condition 支持：
ReentrantLock 支持 Condition，這是一種比 Object 的 wait() 和 notify() 更靈活的等待/通知機制。可以使用 newCondition() 方法來創建一個 Condition 實例，並在 await() 和 signal() 方法中使用。

## Semaphore訊號/訊號量
`java.util.concurrent` 包中的一個同步工具，用於控制對共享資源的訪問量。通過使用信號量，你可以限制同一時間內有多少個執行緒能夠訪問某些資源，這對於管理多執行緒環境中的資源競爭非常有用。

### 基本概念
Semaphore 通常被比作一個具有一定數量“許可證”（permits）的計數器。每個許可證允許一個執行緒訪問受限的資源。當執行緒完成工作後，它會歸還許可證，使得其他等待的執行緒可以繼續工作。

初始許可證數量：Semaphore 被創建時需要指定一個初始的許可證數量，這個數字決定了同時允許多少個執行緒訪問受限資源。

acquire()：當一個執行緒希望訪問資源時，它會調用 acquire() 嘗試獲取一個許可證。如果許可證不足，則執行緒會被阻塞，直到有許可證可用為止。

release()：當執行緒完成對資源的訪問時，調用 release() 來釋放許可證。這樣等待中的其他執行緒就可以獲取許可證並繼續工作。

### 公平模式
```java=
private static final Semaphore semaphore = new Semaphore(NUM_PERMITS,true);
```
第一個參數是初始的PERMITS數量
第二個參數則是是否設置為公平模式。
雖然不公平模式的效能會比較好，但平常建議使用公平模式有它的原因在：

#### 公平模式的好處
1. 防止執行緒飢餓：
公平模式確保等待最久的執行緒優先獲取許可證，這樣可以防止某些執行緒長時間得不到資源而無法執行，避免了飢餓問題。在一些情況下，如果使用非公平模式，有些執行緒可能會長時間無法獲得資源，導致系統資源利用率不均衡。
2. 可預測性：
公平模式提供了可預測的行為，因為它保證了執行緒將按照它們請求資源的順序獲得資源。這對於需要確保多個執行緒以公平方式訪問資源的應用場景特別有用，例如一些需要嚴格遵循時間順序的操作。
3. 提升系統穩定性：
公平模式有助於提升系統的穩定性，因為它避免了某些執行緒因無法獲得資源而陷入長時間等待的情況。這樣可以確保所有執行緒都能定期地進行工作，保持系統的平衡和穩定。
#### 何時使用公平模式
公平模式特別適合以下場景：
1. 任務必須公平地處理：
當應用程序要求所有任務公平地獲得資源時，例如在多個用戶或請求之間需要公平分配資源的場景。
2. 長時間運行的系統
在需要長時間運行的系統中，公平模式可以防止某些執行緒長時間無法獲得資源，這對於保持系統的穩定性非常重要。
3. 需要避免飢餓問題的場景
當系統中有大量執行緒競爭有限的資源時，公平模式可以確保每個執行緒都能定期獲得資源，避免某些執行緒永遠無法執行。
#### 公平模式的潛在缺點
雖然公平模式提供了上述好處，但也存在一些潛在的缺點：

1. 性能開銷：
由於公平模式需要維護一個等待隊列，並按順序分配資源，因此它可能會帶來額外的性能開銷。相比非公平模式，公平模式可能會稍微降低吞吐量。

2. 可能的資源等待：
在某些高競爭場景中，由於嚴格的先來先得順序，某些執行緒可能會不得不等待更長的時間才能獲取資源。
## Callable
`java.util.concurrent` 包中的一個介面，類似於 Runnable，但它允許在**任務完成後返回一個結果或拋出一個異常**。
Callable 的引入是為了補充 Runnable 的不足，因為 Runnable 的 run() 方法不返回結果，也不會拋出受檢異常。
### Future
`java.util.concurrent` 包中的一個介面，旨在表示一**個異步計算的結果**。當你提交一個 Callable 任務或者 Runnable 任務到執行器（ExecutorService）時，執行器會立即返回一個 Future 對象，通過這個對象可以**檢查任務是否完成(`future.isDone()`)、獲取計算結果(`future.get()`)，或者取消任務(`future.cancel()`)**。
## thread的sleep與wait差異是什麼?
在 Java 中，sleep() 和 wait() 都是用來讓執行緒進入等待狀態的方法，但它們的作用和使用場景有很大的不同。這裡將詳細介紹兩者之間的主要區別。

1. 所屬類不同
**sleep()**:Thread 類的一個靜態方法。調用 Thread.sleep() 是讓當前執行緒進入休眠狀態，並保持這個狀態一段時間。
**wait()**:Object 類的一個實例方法。每個 Java 對象都繼承了這個方法。調用 wait() 是讓當前執行緒進入等待狀態，並且等待其他執行緒調用同一對象的 notify() 或 notifyAll() 方法來喚醒它。
**notify()**:Java 中 Object 類別的一個方法，用來喚醒那些正在該對象的監視器（monitor）上等待的單個執行緒。當執行緒需要等待某個條件時，它通常會調用 wait() 方法進入等待狀態，而 notify() 則是用來喚醒這些等待的執行緒。
特別注意的是，**notify() 必須在同步塊或同步方法中調用**，這是因為 wait() 和 notify() 都**依賴於對象的監視器鎖（monitor lock）**，只有持有該鎖的執行緒才能調用這些方法。
當一個執行緒調用 notify() 方法時，會喚醒正在等待該對象監視器的其中一個執行緒。
被喚醒的執行緒不會立刻繼續執行，而是會嘗試重新獲取對象的監視器鎖。一旦它成功獲取到鎖，它才能繼續執行。
而且notify() **僅喚醒單個執行緒**，如果有多個執行緒在等待，則被喚醒的對象是由 JVM 決定的（不保證是哪一個）。

2. 使用目的和行為
**sleep():**
目的: 讓執行緒暫停執行指定的時間，然後自動恢復執行。
行為: 當 sleep() 被調用後，執行緒進入休眠狀態，這段期間執行緒不會消耗 CPU 時間，但它保持著對任何鎖的所有權。sleep() 的時間結束後，執行緒會自動喚醒並進入就緒狀態（等待 CPU 調度器再次分配 CPU 給它）。
喚醒方式: sleep() 可以通過時間到達自動喚醒，也可以被中斷（InterruptedException）來提前結束。
**wait():**
目的: 讓執行緒進入等待狀態，直到某一特定條件被滿足（例如其他執行緒調用 notify() 或 notifyAll()）時才會被喚醒。
行為: 當執行緒調用 wait() 時，它會釋放當前持有的對象鎖，並進入該對象的等待隊列，等待其他執行緒調用 notify() 或 notifyAll() 來喚醒它。執行緒被喚醒後，必須重新獲得該對象的鎖才能繼續執行。
喚醒方式: 通常通過其他執行緒調用 notify() 或 notifyAll() 來喚醒，也可以被中斷（InterruptedException）提前結束。
3. 鎖的影響
sleep(): 不會釋放已經持有的鎖。因此，執行緒在 sleep() 期間仍然擁有它之前獲得的任何鎖，這可能會導致其他執行緒被阻塞，等待該鎖的釋放。
wait(): 在調用 wait() 時，執行緒會釋放它持有的對象鎖，並進入該對象的等待隊列。這樣，其他執行緒可以獲得該鎖並繼續執行，從而實現執行緒間的協作。

總結：
1. 功能: 
sleep() 用於讓當前執行緒休眠一段固定時間；
wait() 用於執行緒同步，讓當前執行緒等待某個條件。
2. 鎖: 
sleep() 不釋放鎖。
wait() 會釋放鎖。
3. 喚醒機制: 
sleep() 通過時間結束或中斷來喚醒；
wait() 需要通過 notify()、notifyAll() 或中斷來喚醒。
這些區別使得 sleep() 更適合用於暫時停止執行，而 wait() 更適合用於在執行緒之間協調操作上。
## 名詞介紹
### Concurrent 並發性(中國稱併發)
主要是**一個 CPU 會去做多件事，但是同一個時間點之內只會做一件事。**
一個人可能會去做 Job1、Job2、Job3這三件事，但是在一個時間點內只會做一個 Job，像是早上做 Job1、下午做 Job2、晚上做 Job3不斷的切換。

**實際的案例：**
Concurrency：Multi-thread，多執行緒（多執行緒）程式會把一個工作拆分成數個子任務，並利用好幾個thread同時運行子任務。
**以工作的分配情形解釋：**
相同的工作集合，一起完成同一份工作，互相合作，做團體業績。

因此**Concurrent 的目標是希望能做更多的事**。
### Parallel 平行性(又稱並行)
Parallel 主要是會有**多個CPU在同一個時間點內會去做多件事**。
例如會有三個人同時分別在做 Job1、Job2、Job3 的事情。

**實際的案例：**
Parallelism：Load balancing，負載平衡機制，例如為了把網站流量分散，架設了多個相同內容的網頁伺服器，並將訪客平均分配到這些伺服器上。
**以工作的分配情形解釋：**
不同的工作集合，各自完成自己的工作，不互相干擾，有各自的考績。

因此**Parallel的目標是希望能把事情更快速的做完**。
### Dead Lock死鎖
如果兩個執行緒互相等待對方完成，就會發生死鎖，導致程序無限期地掛起。


## 參考資料
https://java.4-x.tw/java-15/java-15-2
https://ithelp.ithome.com.tw/users/20103456/ironman/1766
https://ithelp.ithome.com.tw/articles/10205277
https://medium.com/mr-efacani-teatime/concurrency%E8%88%87parallelism%E7%9A%84%E4%B8%8D%E5%90%8C%E4%B9%8B%E8%99%95-1b212a020e30
https://www.youtube.com/watch?v=WldMTtUWqTg&ab_channel=RiddhiDutta