## 關鍵字Synchronized
關鍵字`Synchronized`、`Object.wat()`和`Object.notify()`是在jdk1.5之前用的多線程同步控制的方式,jdk1.5之后就提供了如下的`java.util.concurrent`工具包(簡稱為`juc`并發工具包),可以利用如下的新的工具來實現多線程間的同步。
如下代碼,可以知道其使用場景及作用:
~~~
// 類T
class T {
// 靜態對象,是在類上的,只有一個
private static Object lock = new Object();
@SneakyThrows
public void doSomething(){
// 鎖靜態對象,只有一個,是系統一個鎖
synchronized (lock){
lock.wait();
}
}
public void doSomething2(){
// 鎖類對象,只有一個,是同一個鎖
synchronized (T.class){
// 做一些事情
lock.notify();
}
}
// 修飾在實例方法上,實則是在調用對象上加鎖,調用對象不同則鎖不同
public synchronized void doSomething3(){
}
// 修飾在靜態方法上,實則是在當前類對象上加鎖,只有一個鎖
public static synchronized void doSomething4(){
}
}
~~~
## 重入鎖 ReentrantLock
### 定義
重入鎖可以允許一個線程連續2次獲取同一把鎖,當然解鎖也需要解鎖2次。(關鍵字synchronized也是重入鎖)
### 重要方法

* lock() 獲取鎖,如果鎖被占用,則等待
* lockInterruptibly() 獲取鎖,但優先響應中斷
可以利用中斷來直接退出等待鎖,釋放資源,防止死鎖
* tryLock() 嘗試獲取鎖,理解返回結果;獲取成功返回true,失敗返回false
* tryLock(timeout,unit) 給定時間內嘗試獲取鎖
* unlock() 釋放鎖
### 公平鎖
構造方法內傳遞fair=true,則返回的是公平鎖,默認是非公平鎖,關鍵字synchronized也是非公平鎖。
```
ReentrantLock lock = new ReentrantLock(true);
```
公平鎖會按鎖的線程等待隊列的順序,公平的讓線程獲取到鎖,所以性能會比非公平鎖低。
## 等待隊列 Condition
`Condition`是重入鎖的好搭檔,讓鎖可以支持多個等待隊列。提供了和`Object.wait()`、`Object.notify()`和對應功能的方法`Condition.await()`、`Condition.signal()`和`Condition.signalAll()`

具體使用可參考`ArrayBlockQueue`的實現。
## 信號量 Semaphore
無論是內部鎖synchronized還是重入鎖ReentrantLock,一次都只允許一個線程訪問臨界區資源。而
`Semaphore`可以同時允許多個線程同時訪問某一個資源。
構造方法如下:
~~~
public Semaphore(int permits);// 第一個參數信號量數量
public Semaphore(int permits, boolean fair); // 第二個參數是否公平
~~~
主要方法如下:
```
public void acquire(); // 獲取許可,阻塞直到獲取到或者線程被中斷
public void acquireUninterruptibly(); // 獲取許可,阻塞直到獲取到,不響應中斷
public boolean tryAcquire(); // 嘗試獲取許可,直接返回結果
public boolean tryAcquire(long timeout,TimeUnit unit); // 在限定時間內嘗試獲取許可
public void release(); // 釋放許可
```
是一個有效的流量控制工具,它基于AQS共享鎖實現。常常用它來控制對有限資源的訪問。
* 每次使用資源前,先申請一個信號量,如果資源數不夠,就會阻塞等待;
* 每次釋放資源后,就釋放一個信號量。
## 讀寫鎖 ReadWriteLock
讀寫分離鎖可以減少鎖競爭,提升性能,適合在讀多寫少的場景使用。
是JDK5中提供的,實現類有`ReentrantReadWriteLock`。

~~~
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
Lock readLock = readWriteLock.readLock();// 讀鎖,共享鎖
Lock writeLock = readWriteLock.writeLock();// 寫鎖,獨占鎖
~~~
## 倒計數器 CountDownLatch
CountDownLatch是一個非常實用的多線程控制工具類,可以讓某一個線程等待,直到倒計數器結束。典型的場景,如火箭發射時的倒數計時(等待各個發射細節就位)。

~~~
CountDownLatch countDownLatch = new CountDownLatch(3);
// 注意:相關檢查是多線程并發執行的
// 檢查1
// doCheck1()
countDownLatch.countDown();
// 檢查2
// doCheck2()
countDownLatch.countDown();
// 檢查3
// doCheck3()
countDownLatch.countDown();
// 等待檢查完成,再執行發射邏輯
countDownLatch.await();
// 點火發射
// doFire()
~~~
## 循環柵欄 CyclicBarrier
`CyclicBarrier`也是一種多線程并發控制的工具,和`CountDownLatch`類似,前者可以重復使用,而后者只使用一次。

例子:
~~~
public static void main(String[] args) {
CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2, () -> {
System.out.println("滿2個,group1執行完了");
});
CyclicBarrier cyclicBarrier2 = new CyclicBarrier(3, () -> {
System.out.println("滿3個,group2執行完了");
});
for(int i=0;i<100;i++) {
boolean b = i % 2 == 0;
Thread t1 = new Thread(() -> {
try {
if(b){
// 偶數,則到籬笆1
cyclicBarrier1.await();
}else{
// 偶數,則到籬笆2
cyclicBarrier2.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "跑完了");
});
t1.setName("t"+i);
t1.start();
}
}
~~~
輸出:
```
滿2個,group1執行完了
t2跑完了
t0跑完了
滿3個,group2執行完了
t5跑完了
t1跑完了
t3跑完了
滿2個,group1執行完了
t6跑完了
t4跑完了
滿2個,group1執行完了
t10跑完了
t8跑完了
```
## 阻塞工具類 LockSupport
`LockSupport`是一個非常方便使用的線程阻塞工具,可以在任意線程內讓線程阻塞,不需要先獲得某個對象的鎖,也不會拋出中斷異常。(底層實現類似信號量_counter計數,當park時,這個變量置為了0,當unpark時,這個變量置為1)
~~~
LockSupport.park(); // 阻塞當前線程,也有限時等待,還支持中斷響應
LockSupport.unpark(Thread.currentThread()); // 恢復某個線程
~~~
## 參考文檔
* 書籍:葛一鳴 *《Java高并發程序設計第二版》
- 面試突擊
- Java虛擬機
- 認識字節碼
- 000Java發展歷史
- 000Macos10.15.7上編譯OpenJDK8u
- 001熟悉Java內存區域
- 002熟悉HotSpot中的對象
- 003Java如何計算對象大小
- 004垃圾判定算法與4大引用
- 005回收堆和方法區中對象
- 006垃圾收集算法
- 007HotSpot虛擬機垃圾算法實現篇1
- 007HotSpot虛擬機垃圾算法實現篇2
- 007HotSpot虛擬機垃圾算法實現篇3
- 008垃圾收集器
- 009內存分配與回收策略
- 010Java虛擬機相關工具
- 011調優案例分析
- 012一次IDEA的啟動速度調優
- 013類文件Class的結構
- 014熟悉字節碼指令
- 015類加載機制(過程)
- 016類加載器
- IDEA的JVM參數
- Java基礎
- Java自動裝箱與拆箱
- Java基礎數據類型
- Java方法的參數傳遞
- Java并發
- 001走入并行的世界
- 002并行程序基礎
- 003熟悉Java內存模型JMM
- 004Java并發之volatile關鍵字
- 005線程池入門到精通
- 006Java多線程間的同步控制方法
- 007Java維基準測試框架JMH
- 008Java并發容器
- 009Java的線程實現
- 010Java關鍵字synchronized
- 011一些并行模式的熟悉
- 單例模式和不變模式
- 生產者消費者模式
- Future模式
- 012一些并行算法的熟悉
- 面試總結
- 長亮一面