### 鎖\(synchronized\)
作用:關鍵字synchronized的作用是實現線程間的同步;
JVM規范定義了線程對主存的操作指令:read,load,use,assign,store,write
JavaSE1.6中,鎖一共有4種狀態,級別從低到高依次是:無鎖狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態,這幾個狀態會隨著競爭情況逐漸升級。鎖可以升級但不能降級,意味著偏向鎖升級成輕量級鎖后不能降級成偏向鎖。這種鎖升級卻不能降級的策略,目的是為了提高獲得鎖和釋放鎖的效率
### 鎖的內存語義
* synchronized的底層是使用操作系統的mutex lock實現的;
* 內存可見性:同步塊的可見性是由“如果對一個變量執行lock操作,將會清空工作內存中此變量的值,在執行引擎使用這個變量前需要重新執行load或assign操作初始化變量的值”、“對一個變量執行unlock操作之前,必須先把此變量同步回主內存中(執行store和write操作)”這兩條規則獲得的;
* 操作原子性:持有同一個鎖的兩個同步塊只能串行地進入;
* 當線程釋放鎖時,JMM會把該線程對應的本地內存中的共享變量刷新到主內存中;
* 當線程獲取鎖時,JMM會把該線程對應的本地內存置為無效。從而使得被監視器保護的臨界區代碼必須從主內存中讀取共享變量;
### Synchronized可重入鎖
當線程請求一個未被持有的鎖時,JVM記錄鎖的持有者且將計數器置為1,如果同一個線程再次獲取這個鎖計數器遞增;當線程退出同步代碼塊時,計數器遞減;當計數值為0時鎖釋放;
### 假喚醒
由于莫名其妙的原因,線程有可能在沒有調用過notify\(\)和notifyAll\(\)的情況下醒來。這就是所謂的假喚醒(spurious wakeups)
### 鎖釋放和鎖獲取的內存語義
* 線程A釋放一個鎖,實質上是線程A向接下來將要獲取這個鎖的某個線程發出了(線程A對共享變量所做修改的)消息。
* 線程B獲取一個鎖,實質上是線程B接收了之前某個線程發出的(在釋放這個鎖之前對共享變量所做修改的)消息。
* 線程A釋放鎖,隨后線程B獲取這個鎖,這個過程實質上是線程A通過主內存向線程B發送消息
### 用法
Java中的每一個對象都可以作為鎖:
* 對于同步方法,鎖是當前實例對象;
* 對于靜態同步方法,鎖是當前對象的Class對象;
* 對于同步方法塊,鎖是Synchonized括號里配置的對象\(monitorenter指令是在編譯后插入到同步代碼塊的開始位置,而monitorexit是插入到方法結束處和異常處, JVM要保證每個monitorenter必須有對應的monitorexit與之配對\);
* JVM基于進入和退出Monitor對象來實現方法同步和代碼塊同步。代碼塊同步是使用monitorenter和monitorexit指令實現的,monitorenter指令是在編譯后插入到同步代碼塊的開始位置,而monitorexit是插入到方法結束處和異常處。任何對象都有一個monitor與之關聯,當且一個monitor被持有后,它將處于鎖定狀態;JVM要保證每個monitorenter必須有對應的monitorexit與之配對;
* synchronized同步塊對同一個線程來說是可重入的,不會出現自己把自己鎖死的問題;
* 如果鎖對象是靜態對象或Class時,可以認為是一個JVM進程全局鎖;
### 總結
* synchronized特點:保證內存可見性、操作原子性
* synchronized影響性能的原因:
1、 加鎖解鎖操作需要額外操作;
2、 互斥同步對性能最大的影響是阻塞的實現,因為阻塞涉及到的掛起線程和恢復線程的操作都需要轉入內核態中完成(用戶態與內核態的切換的性能代價是比較大的)
* synchronized鎖:對象頭中的Mark Word根據鎖標志位的不同而被復用
* 偏向鎖:在只有一個線程執行同步塊時提高性能。Mark Word存儲鎖偏向的線程ID,以后該線程在進入和退出同步塊時不需要進行CAS操作來加鎖和解鎖,只需簡單比較ThreadID。特點:只有等到線程競爭出現才釋放偏向鎖,持有偏向鎖的線程不會主動釋放偏向鎖。之后的線程競爭偏向鎖,會先檢查持有偏向鎖的線程是否存活,如果不存活,則對象變為無鎖狀態,重新偏向;如果仍存活,則偏向鎖升級為輕量級鎖,此時輕量級鎖由原持有偏向鎖的線程持有,繼續執行其同步代碼,而正在競爭的線程會進入自旋等待獲得該輕量級鎖
* 輕量級鎖:在當前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,嘗試拷貝鎖對象目前的Mark Word到棧幀的Lock Record,若拷貝成功:虛擬機將使用CAS操作嘗試將對象的Mark Word更新為指向Lock Record的指針,并將Lock record里的owner指針指向對象的Mark Word。若拷貝失敗:若當前只有一個等待線程,則可通過自旋稍微等待一下,可能持有輕量級鎖的線程很快就會釋放鎖。 但是當自旋超過一定的次數,或者一個線程在持有鎖,一個在自旋,又有第三個來訪時,輕量級鎖膨脹為重量級鎖
* 重量級鎖:指向互斥量(mutex),底層通過操作系統的mutex lock實現。等待鎖的線程會被阻塞,由于Linux下Java線程與操作系統內核態線程一一映射,所以涉及到用戶態和內核態的切換、操作系統內核態中的線程的阻塞和恢復。
#### Java對象頭
鎖存在Java對象頭里。如果對象是數組類型,則虛擬機用3個Word(字寬)存儲對象頭,如果對象是非數組類型,則用2字寬存儲對象頭。在32位虛擬機中,一字寬等于四字節,即32bit
| 長度 | 內容 | 說明 |
| :--- | :--- | :--- |
| 32/64bit | Mark Word | 存儲對象的hashCode或鎖信息等 |
| 32/64bit | Class Metadata Address | 存儲到對象類型數據的指針 |
| 32/64bit | Array length | 數組的長度(如果當前對象是數組) |
Java對象頭里的Mark Word里默認存儲對象的HashCode,分代年齡和鎖標記位。32位JVM的Mark Word的默認存儲結構如下:
| | 25bit | 4bit | 1bit是否是偏向鎖 | 2bit鎖標志位 |
| :--- | :--- | :--- | :--- | :--- |
| 無鎖狀態 | 對象的hashCode | 對象分代年齡 | 0 | 01 |
在運行期間Mark Word里存儲的數據會隨著鎖標志位的變化而變化。Mark Word可能變化為存儲以下4種數據:

在64位虛擬機下,Mark Word是64bit大小的,其存儲結構如下:

### 鎖消除
```
synchronized (new Object()) {}
```
這段代碼其實不會執行任何操作,你的編譯器會把它完全移除掉,因為編譯器知道沒有其他的線程會使用相同的監視器進行同步


- 簡介
- 概述
- 進程vs線程
- 資源限制
- 有關并行的兩個定律
- 線程同步和阻塞
- 線程阻塞
- 線程的特性
- 守護線程
- 線程異常
- Thread
- 線程狀態
- 線程中斷
- wait¬ify
- suspend&resume
- join&yield
- notify¬ifyAll
- Thread.sleep
- 線程任務
- Runnable
- Callable
- Future模式
- FutureTask
- 線程實現方式
- 內核線程實現
- 用戶線程實現
- 混合實現
- Java線程的實現
- java與協程
- 纖程-Fiber
- 線程調度
- 多線程協作方式
- 阻塞
- 放棄
- 休眠
- 連接線程
- 線程估算公式
- 線程活躍性
- 死鎖
- 線程安全性
- 對象的發布與逸出
- 構造方法溢出
- 線程封閉
- 對象的可變性
- 原子性
- 原子操作
- CPU原子操作原理
- 總線鎖
- 緩存鎖
- JAVA如何實現原子操作
- long和double讀寫操作原子性
- Adder和Accumulator
- 線程性能
- 同步工具類
- 閉鎖
- CountDownLatch
- FutureTask
- 信號量
- 柵欄
- CyclicBarrier
- Exchanger
- 并發編程
- volatile
- synchronized
- 無鎖
- 偏向鎖
- 輕量級鎖
- 鎖的優缺點對比
- 鎖升級
- 鎖消除
- Monitor
- synchronized語法
- Mutex Lock
- synchronized實踐問題
- synchronized&ReentrantLock
- Lock
- ReentrantLock
- Condition
- 讀寫鎖
- ReadWriteLock
- StampedLock
- 線程池
- Executor
- ExecutorService
- Executors
- ThreadPoolExecutor
- RejectedExecutionHandler
- ThreadFactory
- 線程池大小公式
- 動態調整線程池大小
- Fork/Join框架
- ForkJoinPool
- CompletableFuture
- JUC并發工具包
- LockSupport
- 延時任務與周期任務
- Timer
- TimerTask
- 異構任務并行化
- CompletionService
- volatile和synchronized比較
- 鎖優化
- 鎖相關概念
- 悲觀鎖(排它鎖)
- 樂觀鎖
- 自旋鎖
- 樂觀鎖vs悲觀鎖
- JVM鎖優化-鎖消除
- ThreadLocal
- InheritableThreadLocal
- TransmittableThreadLocal
- ThreadLocalRandom
- 無鎖
- AtomicInteger
- Unsafe
- AtomicReference
- AtomicStampedReference
- AtomicIntegerArray
- AtomicIntegerFieldUpdater
- 無鎖Vector
- LongAdder
- LongAccumulator
- 常見鎖類型
- 悲觀鎖&獨占鎖
- 樂觀鎖
- 樂觀鎖vs悲觀鎖
- 自旋鎖vs適應性自旋鎖
- 公平鎖vs非公平鎖
- 可重入鎖vs非可重入鎖
- 獨享鎖vs共享鎖
- 互斥鎖
- CAS
- AQS介紹
- AQS深入剖析
- AQS框架
- AQS核心思想
- AQS數據結構
- 同步狀態State
- ReentrantLock vs AQS
- AQS與ReentrantLock的關聯
- ReentrantLock具體實現
- 線程加入等待隊列
- 等待隊列中線程出隊列時機
- 如何解鎖
- 中斷恢復后的執行流程
- ReentrantLock的可重入應用
- JUC中的應用場景
- 自定義同步工具
- CLH鎖
- 并發框架
- Akka
- Disruptor-無鎖緩存框架
- 常見面試題
- 兩個線程交替打印A和B
- 附錄