目錄
- 什么是JUC
- 行程與執行緒
- 執行緒的狀態
- wait和sleep區別
- 并發與并行
- Lock 和 Synchronized
- 執行緒間的通信 等待/通知
- Lock 和 ReadWriteLock 介面
- 集合的執行緒安全
- Vector
- Collections
- CopyOnWriteArrayList(重點)
- Callable&Future 介面
- Callable介面
- Future介面 實作類 FutureTask
- JUC輔助類
- CountDownLatch 倒計數閂鎖
- CyclicBarrier 回圈屏障
- Semaphore 信號量
- 讀寫鎖 ReadWriteLock介面的實作類 ReentrantReadWriteLock
- 阻塞佇列 BlockingQueue介面
- 為什么需要 BlockingQueue?
- BlockingQueue介面核心方法
- 常用阻塞佇列
- ThreadPool 執行緒池
- 執行緒池引數說明
- 拒絕策略
- CallerRunsPolicy
- AbortPolicy
- DiscardPolicy
- DiscardOldestPolicy
- 執行緒池的種類和創建
- newCacheThreadPool(常用) 可快取執行緒池
- newFixedThreadPool(常用)
- newSingleThreadExecutor(常用)
什么是JUC
JDK1.5出現的,用來處理執行緒的工具包
行程與執行緒
行程:指在系統中正在運行的一個應用程式;程式一旦運行就是行程;行程一 -資源分配的最小單位,
執行緒:系統分配處理器時間資源的基本單元,或者說行程之內獨立執行的一個單元執行流,執行緒一程式執行的最小單位,
執行緒的狀態
NEW(新建)、RUNNABLE(可運行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(定時等待)、TERMINATED(終止)
wait和sleep區別
①sleep是Thread的靜態方法,wait是Object的方法任何物件實體都能呼叫
②sleep不會釋放鎖也不會占用鎖,wait會釋放鎖,但呼叫前提是當前執行緒有鎖
③都可以被interrupted方法中斷
并發與并行
解釋一:并行是指兩個或者多個事件在同一時刻發生;而并發是指兩個或多個事件在同一時間間隔發生,
解釋二:并行是在不同物體上的多個事件,并發是在同一物體上的多個事件,
解釋三:在一臺處理器上“同時”處理多個任務,在多臺處理器上同時處理多個任務,
并發:指的是多個事情,在同一時間段內同時發生了,多個任務之間是互相搶占資源的,
并行:指的是多個事情,在同一時間點上同時發生了,多個任務之間是不互相搶占資源的,
只有在多CPU的情況中,才會發生并行,否則,看似同時發生的事情,其實都是并發執行的,
Lock 和 Synchronized
①Synchronized是Java關鍵字,是同步鎖,可修飾代碼塊、方法(不被子類繼承)、靜態方法、類
②發生例外會自動釋放鎖,如果被阻塞會一直無限期等待
③synchronized實作同步的基礎:Java中的每一個物件都可以作為鎖,具體表現為以下3種形式,
對于普通同步方法,鎖是當前實體物件,
對于靜態同步方法,鎖是當前類的class物件,
對于同步方法塊,鎖是Synchonized括號里配置的物件,
①Lock是一個介面,通過這個介面的實作類可以實作同步訪問,必須要用戶手動釋放鎖,如果不釋放會發生死鎖,
②Lock可以讓等待鎖的執行緒回應中斷,Synchronized卻不行,等待執行緒會一直等待下去
③Lock可以知道有沒有成功獲取鎖
④Lock可以提高多個執行緒進行讀操作的效率
執行緒間的通信 等待/通知
①關鍵字Synchronized通過wait()/notify()兩個方法實作等待/通知,
②Lock鎖的newCondition()方法回傳Condition物件,Condition類也可以實作等待/通知,
區別:
notify()隨機喚醒,使用Condition可以選擇性通知,condition比較常用的兩個方法:
? await()會使當前執行緒等待,同時會釋放鎖,當其他執行緒呼叫 signal()時,執行緒會重新獲得鎖并繼續執行,
? signal()用于喚醒一個等待的執行緒,
Lock 和 ReadWriteLock 介面
Lock的實作類ReentrantLock可重入鎖
ReadWriteLock的實作類ReentrantReadWriteLock可重入讀寫鎖,讀寫鎖分開,從而使得執行緒可以同時進行讀操作
注意:
有一個執行緒占用讀鎖,其他執行緒申請寫鎖會等待讀鎖釋放
有一個執行緒占用寫鎖,其它執行緒申請寫鎖或讀鎖會一直等待釋放寫鎖
集合的執行緒安全
Vector
所有的方法,都是通過synchronized修飾,方法鎖,實際也是物件鎖,安全
Collections
Collections 提供了方法 synchronizedList 保證 list 是同步執行緒安全的
都是通過mutex物件加鎖,一樣,效率和Vector一樣低
CopyOnWriteArrayList(重點)
寫時復制的方法,來控制的讀寫分離,但是存在的問題就是,會出現臟讀的現象
Callable&Future 介面
創建執行緒的方法,一種是通過Threa類,另一種使用Runnable創建執行緒,無法使執行緒回傳結果,而Callable可以,
Callable介面
Runnable只需要實作不回傳任何內容的run()方法,對于Callable需要實作完成時回傳結果的call()方法,
call()方法可以引發例外,而run()不能
new Thread(Runnable r)不能直接替換Runnable,因為Thread類構造方法沒有Callable
Future介面 實作類 FutureTask
當 call()方法完成時,結果必須存盤在主執行緒已知的物件中,以便主執行緒可以知道該執行緒回傳的結果,為此,可以使用 Future 物件,
- public boolean cancel(boolean mayInterrupt):用于停止任務
- public Object get() 拋出 InterruptedException,ExecutionException:用于獲取任務的結果,
- public boolean isDone():如果任務完成,則回傳 true,否則回傳 false
在主執行緒中需要執行比較耗時的操作時,但又不想阻塞主執行緒時,可以把這些作業交給 Future 物件在后臺完成
一般 FutureTask 多用于耗時的計算,主執行緒可以在完成自己的任務后,再去獲取結果
get 方法而獲取結果只有在計算完成時獲取,否則會一直阻塞直到任務轉入完成狀態,然后會回傳結果或者拋出例外
public class CallableDemo {
static class Mythread1 implements Runnable{
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"執行緒進入了run方法");
} catch (Exception e) {
e.printStackTrace();
}
}
}
static class Mythread2 implements Callable{
@Override
public Long call() throws Exception {
try {
System.out.println(Thread.currentThread().getName()+"執行緒進入了call方法,開始準備睡覺");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"睡醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
return System.currentTimeMillis();
}
}
public static void main(String[] args) throws Exception {
Runnable runnable = new Mythread1();
Callable callable = new Mythread2();
FutureTask<Long> futureTask = new FutureTask<Long>(callable);
//執行緒一
new Thread(runnable,"執行緒一").start();
//執行緒二
new Thread(futureTask,"執行緒二").start();
for (int i = 0; i < 10; i++) {
Long result = futureTask.get(); // 阻塞等待計算結果
System.out.println(result);
}
}
}
JUC輔助類
CountDownLatch 倒計數閂鎖
一個或者一組執行緒在開始執行操作之前,必須要等到其他執行緒執行完才可以
public class CountDownLatchDemo {
public static void main(String[] args) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try {
if (Thread.currentThread().getName().equals("同學6")){
Thread.sleep(2000);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"離開了");
countDownLatch.countDown();
},"同學"+i).start();
}
System.out.println("主執行緒睡覺");
countDownLatch.await();
System.out.println("全部離開了,現在的計數器為"+countDownLatch.getCount());
}
}
CyclicBarrier 回圈屏障
CyclicBarrier 的構造方法第一個引數是目標障礙數,每次執行 CyclicBarrier 一次障礙數會加一,如果達到了目標障礙數,才會執行 cyclicBarrier.await()之后的陳述句,
public class CyclicBarrierDemo {
private final static int NUMBER = 7;
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
System.out.println("集齊" + NUMBER + "顆龍珠,現在召喚神龍!!!!!!!!!");
});
for (int i = 1; i <= 7; i++) {
new Thread(() -> {
try {
if (Thread.currentThread().getName().equals("龍珠3號")) {
System.out.println("龍珠3號搶奪戰開始,悟空變身超級賽亞人");
Thread.sleep(5000);
System.out.println("龍珠3號搶奪戰結束,悟空贏了,拿到了龍珠3號");
} else {
System.out.println(Thread.currentThread().getName() + "收集到了!");
}
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}, "龍珠" + i + "號").start();
}
}
}
Semaphore 信號量
Semaphore 的構造方法中傳入的第一個引數是最大信號量(可以看成執行緒池),每個信號量初始化為一個最多只能分發一個許可證,使用 acquire 方法獲得許可證,release 方法釋放許可,
public class SemaphoreDemo {
// 搶車位, 10 部汽車 3 個停車位
public static void main(String[] args) throws Exception {
// 定義3個車位
Semaphore semaphore = new Semaphore(3);
// 模擬10輛車
for (int i = 0; i < 10; i++) {
Thread.sleep(100);
// 停車
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"找車位ing....");
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"汽車停車成功!");
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName()+"溜了溜了");
semaphore.release();
}
},"汽車"+i).start();
}
}
}
讀寫鎖 ReadWriteLock介面的實作類 ReentrantReadWriteLock
允許多個執行緒同時讀取共享資源;但是如果一個執行緒想去寫這些共享資源,就不應該允許其他執行緒對該資源進行讀和寫的操作了,
- 執行緒進入讀鎖的前提條件:
? 沒有其他執行緒的寫鎖
? 沒有寫請求, 或者有寫請求,但呼叫執行緒和持有鎖的執行緒是同一個(可重入鎖), - 執行緒進入寫鎖的前提條件:
? 沒有其他執行緒的讀鎖
? 沒有其他執行緒的寫鎖
讀寫鎖有以下三個重要的特性:
(1)公平選擇性:支持非公平(默認)和公平的鎖獲取方式,吞吐量非公平優于公平,
(2)重進入:讀鎖和寫鎖都支持執行緒重進入,
(3)鎖降級:遵循獲取寫鎖、獲取讀鎖再釋放寫鎖的次序,寫鎖能夠降級成為讀鎖,最后釋放讀鎖,
public class ReentrandReadWriteLockDemo {
static class Myche {
private volatile Map<String, Object> map = new HashMap<>();
private ReadWriteLock rwLock = new ReentrantReadWriteLock();
public void put(String key, Object value) {
try {
rwLock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "正在寫操作" + key);
TimeUnit.MICROSECONDS.sleep(1000);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "寫完了" + key);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rwLock.writeLock().unlock();
}
}
public Object get(String key) {
rwLock.readLock().lock();
Object result = null;
try {
System.out.println(Thread.currentThread().getName() + "正在讀取操作" + key);
TimeUnit.MICROSECONDS.sleep(300);
result = map.get(key);
System.out.println(Thread.currentThread().getName() + "取完了" + key);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock();
}
return result;
}
}
public static void main(String[] args) {
Myche myche = new Myche();
// 讀共享 鎖
for (int b = 0; b < 10; b++) {
int finalI = b;
new Thread(() -> {
myche.get("執行緒" + finalI);
}, "執行緒" + b).start();
}
// 寫獨占 鎖
for (int a = 0; a < 10; a++) {
int finalI = a;
new Thread(() -> {
myche.put("執行緒" + finalI, finalI);
}, "執行緒" + a).start();
}
}
}
阻塞佇列 BlockingQueue介面
為什么需要 BlockingQueue?
好處是我們不需要關心什么時候需要阻塞執行緒,什么時候需要喚醒執行緒,因為這一切
BlockingQueue 都給你一手包辦了
當佇列是空的,從佇列中獲取元素的操作將會被阻塞
當佇列是滿的,從佇列中添加元素的操作將會被阻塞
常用佇列
? 先進先出(FIFO):先插入的佇列的元素也最先出佇列,類似于排隊的功能,從某種程度上來說這種佇列也體現了一種公平性
? 后進先出(LIFO):后插入佇列的元素最先出佇列,這種佇列優先處理最近發生的事件(堆疊)
BlockingQueue介面核心方法
| 方法型別 | 拋出例外 | 特殊值 | 阻塞 | 超時 |
|---|---|---|---|---|
| 添加 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
| 獲取 | remove() | poll() | take() | poll(time,unit) |
| 檢查 | element() | peek() | \ | \ |
拋出例外:
當阻塞佇列滿時,往佇列添加元素會拋例外 IllegalStateException: Queue full
當阻塞佇列為空時,往佇列里remove()移除元素會拋例外 NoSuchElementException
特殊值:
添加方法,成功回傳true,失敗回傳false
移除方法,成功回傳佇列元素,佇列為慷訓傳null
阻塞:
當阻塞佇列滿時,生產者執行緒往佇列put元素,佇列會一直阻塞生產者執行緒直到成功put 或 回應中斷退出
當阻塞佇列空時,消費者執行緒試圖從佇列take元素,佇列會一直阻塞消費者執行緒直到佇列可用
超時退出:
當阻塞佇列滿時,佇列會一直阻塞到指定時間,超過指定時間執行緒會自動退出
核心方法:
有回傳值:
添加 offer(e)、獲取poll()
阻塞:
添加 put(e)、獲取take()
drainTo() 一次性獲取BlockingQueue所有可用資料物件(可指定數量),提升效率,
常用阻塞佇列
- ArrayBlockingQueue 由陣列結構組成的有界阻塞佇列
- LinkedBlockingQueue 由鏈表結構組成的有界(但大小默認值為integer.MAX_VALUE)阻塞佇列,
- DelayQueue 元素只有當其指定的延遲時間到了,才能夠從佇列中獲取到該元素,
- PriorityBlockingQueue 支持優先級排序的無界阻塞佇列
- SynchronousQueue 不存盤元素的阻塞佇列,也即單個元素的佇列
- LinkedTransferQueue 由鏈表組成的無界阻塞佇列
- LinkedBlockingDeque 由鏈表組成的雙向阻塞佇列
ThreadPool 執行緒池
- 降低資源消耗: 通過重復利用已創建的執行緒降低執行緒創建和銷毀造成的銷耗,
- 提高回應速度: 當任務到達時,任務可以不需要等待執行緒創建就能立即執行,
- 提高執行緒的可管理性: 執行緒是稀缺資源,如果無限制的創建,不僅會銷耗系統資源,還會降低系統的穩定性,使用執行緒池可以進行統一的分配,調優和監控,
- Java 中的執行緒池是通過 Executor 框架實作的,該框架中用到了 Executor,Executors,ExecutorService,ThreadPoolExecutor 這幾個類
執行緒池引數說明
- corePoolSize 執行緒池的核心執行緒數
- maximumPoolSize 能容納的最大執行緒數
- keepAliveTime 空閑執行緒存活時間
- unit 存活的時間單位
- workQueue 存放提交但未執行任務的佇列
- threadFactory 創建執行緒的工廠類
- handler 等待佇列滿后的拒絕策略
有三個重要引數決定拒絕策略:
①corePoolSize 核心執行緒數
②workQueue 阻塞佇列
③maximumPollSize 最大執行緒數
當提交任務數大于 corePoolSize 的時候,會優先將任務放到 workQueue 阻塞佇列中,當阻塞佇列飽和后,會擴充執行緒池中執行緒數,直到達到maximumPoolSize 最大執行緒數配置,此時,再多余的任務,則會觸發執行緒池的拒絕策略了
當提交的任務數大于(workQueue.size() + maximumPoolSize ),就會觸發執行緒池的拒絕策略
拒絕策略
CallerRunsPolicy
當觸發拒絕策略,只要執行緒池沒有關閉的話,則使用呼叫執行緒直接運行任務,一般并發比較小,性能要求不高,不允許失敗,但是,由于呼叫者自己運行任務,如果任務提交速度過快,可能導致程式阻塞,性能效率上必然的損失較大
AbortPolicy
丟棄任務,并拋出拒絕執行 RejectedExecutionException 例外資訊,執行緒池默認的拒絕策略,必須處理好拋出的例外,否則會打斷當前的執行流程,影響后續的任務執行,
DiscardPolicy
直接丟棄,其他啥都沒有
DiscardOldestPolicy
當觸發拒絕策略,只要執行緒池沒有關閉的話,丟棄阻塞佇列 workQueue 中最老的一個任務,并將新任務加入
執行緒池的種類和創建
newCacheThreadPool(常用) 可快取執行緒池
創建一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閑執行緒,若無可回收,則新建執行緒,
特點:
①執行緒池中數量沒有固定,可達到最大值(Interger. MAX_VALUE)
②執行緒池中的執行緒可進行快取重復利用和回收(回收默認時間為 1 分鐘)
③當執行緒池中沒有可用執行緒,會重新創建一個執行緒
newFixedThreadPool(常用)
①執行緒池中的執行緒處于一定的量,可以很好的控制執行緒的并發量
②執行緒可以重復被使用,在顯示關閉之前,都將一直存在
③超出一定量的執行緒被提交時候需在佇列中等待
newSingleThreadExecutor(常用)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/539290.html
標籤:Java
