## volatile
volatile是輕量級的synchronized,它在多處理器開發中保證了共享變量的“可見性”。可見性的意思是當一個線程修改一個共享變量時,另外一個線程能讀到這個修改的值。如果volatile變量修飾符使用恰當的話,它比synchronized的使用和執行成本更低,因為它不會引起線程上下文的切換和調度;
volatile原理是基于CPU內存屏障\(Memory Barrier\)指令實現的;
如果一個變量被volatile關鍵字修飾時:
* Java內存模型確保所有線程看到這個變量的值是一致的;
* 那么對這個變量的寫是將本地內存中的拷貝刷新到共享內存中;
* 對這個變量的讀會有一些不同,讀是無視本地內存拷貝,直接從共享變量中去讀取數據并拷貝到本地工作內存;
volatile并不能真正保證線程安全,它只能確保一個線程修改了共享數據后,其他線程能看到這個改動,即保證的是可見性,但不能保證原子性
### 內存可見性
由于Java內存模型\(JMM\)規定,所有的變量都存放在主內存中,而每個線程都有著自己的工作內存\(高速緩存\);線程在工作時,需要將主內存中的數據拷貝到工作內存中。這樣對數據的任何操作都是基于工作內存\(效率提高\),并且不能直接操作主內存以及其他線程工作內存中的數據,之后再將更新之后的數據刷新到主內存中;
```
1.這里所提到的主內存可以簡單認為是堆內存,而工作內存則可以認為是棧內存;
2.當一個變量被volatile修飾時,任何線程對它的寫操作都會立即刷新到主內存中,并且會強制讓緩存了該變量的線程中的數據清空,必須從主內存重新讀取最新數據;
3.volatile修飾之后并不是讓線程直接從主內存中獲取數據,依然需要將變量拷貝到工作內存中;
```
### 
### 對象訪問過程
當線程訪問某一個對象值的時候,首先通過對象的引用找到對應在堆內存的變量的值,然后把堆內存變量的具體值load到線程本地內存中,建立一個變量副本,之后線程就不再和對象在堆內存變量值有任何關系,而是直接修改副本變量的值,在修改完之后的某一個時刻(線程退出之前),自動把線程變量副本的值回寫到對象在堆中變量

相關操作指令
* read/load:從主存復制變量到當前工作內存
* use/assign:執行代碼,改變共享變量值
* store/write:用工作內存數據刷新主內存相關內容
其中use and assign在線程執行過程中可以多次出現;
但是這一些操作并不是原子性,也就是說,在read and load之后,線程使用的變量值就是自己棧內存中的變量值備份副本了,這時如果主內存count變量發生修改之后,線程工作內存中的值由于已經加載,不會產生對應的變化,所以計算出來的結果會和預期不一樣
_對于volatile修飾的變量,jvm虛擬機只是保證從主內存加載到線程工作內存的值是最新的_
例如線程1,線程2 在進行read and load操作中,發現主內存中count的值都是5,那么都會加載這個最新的值。在線程1對count進行修改之后,會write到主內存中,主內存中的count變量就會變為6
線程2由于已經進行read and load操作,在后續的運算中,使用的均是自己棧內存中的副本,也就是使用5進行的運算,因此進行運算之后,更新主內存count的變量值也為6
這也就是導致兩個線程及時用volatile關鍵字修改之后,仍會存在并發的情況的原因
總結:對于volatile修飾的變量,如果線程A在新值寫入主內存前,線程B已執行了read/load指令\(如果并發的情況下還沒執行read/load指令則會從主內存同步最新值\),那么在線程A將新值寫入主內存后,線程B繼續使用已加載的本地工作內存,這就導致了并發修改的問題
### volatile關鍵字的兩層語義
一旦一個共享變量(類的成員變量、類的靜態成員變量)被volatile修飾之后,那么就具備了兩層語義:
1. 保證了不同線程對這個變量進行操作時的可見性,即一個線程修改了某個變量的值,這新值對其他線程來說是立即可見的;
2. 禁止進行指令重排序;
#### 為什么要使用Volatile
* Volatile變量修飾符如果使用恰當的話,它比synchronized的使用和執行成本會更低,因為它不會引起線程上下文的切換和調度。在多處理器下,為了保證各個處理器的緩存是一致的,就會實現緩存一致性協議,每個處理器通過嗅探在總線上傳播的數據來檢查自己緩存的值是不是過期了,當處理器發現自己緩存行對應的內存地址被修改,就會將當前處理器的緩存行設置成無效狀態,當處理器要對這個數據進行修改操作的時候,會強制重新從系統內存里把數據讀到處理器緩存里;
* 線程之間的通信機制有兩種:共享內存和消息傳遞;在共享內存的并發模型里,線程之間共享程序的公共狀態,線程之間通過寫-讀內存中的公共狀態來隱式進行通信。在消息傳遞的并發模型里,線程之間沒有公共狀態,線程之間必須通過明確的發送消息來顯式進行通信;
說明:自增操作是不具備原子性的,它包括讀取變量的原始值、進行加1操作、寫入工作內存;自增操作不是原子性操作,而且volatile也無法保證對變量的任何操作都是原子性的
```
下面這段話摘自《深入理解Java虛擬機》:
“觀察加入volatile關鍵字和沒有加入volatile關鍵字時所生成的匯編代碼發現,加入volatile關鍵字時,會多出一個lock前綴指令”
lock前綴指令實際上相當于一個內存屏障(也成內存柵欄),內存屏障會提供3個功能:
1)它確保指令重排序時不會把其后面的指令排到內存屏障之前的位置,也不會把前面的指令排到內存屏障的后面;即在執行到內存屏障這句指令時,在它前面的操作已經全部完成;
2)它會強制將對緩存的修改操作立即寫入主存;
3)如果是寫操作,它會導致其他CPU中對應的緩存行無效。
```
### 使用volatile必須具備以下2個條件:
1. 對變量的寫入操作不依賴變量的當前值,或者你能確保只有單個線程更新變量的值;
2. 該變量沒有包含在具有其他變量的不變式中
### volatile的局限性
只能保證內存可見性,不能用于構建原子的復合操作;當一個變量依賴其它的變量,或者當變量的新值依賴于舊值時,就不能是用volatile變量;
- 簡介
- 概述
- 進程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
- 附錄