## 什么是可見性
* 可見性:一個線程對共享變量值的修改,能夠及時地被其他線程看到
* 共享變量:如果一個變量在多個線程的工作內存中都存在副本,那么這個變量就是這幾個線程的共享變量
## Java內存模型(JMM)的介紹
Java內存模型(Java Memory Model)描述了Java程序中各種變量(共享變量)的訪問規則,及在JVM中將變量存儲到內存和從內存中讀取出變量的底層細節
* 1.所有的變量都存儲在主內存中
* 2.每個線程都有自己獨立的工作內存,里面保存該線程使用到的變量的副本(主內存中該變量的一份拷貝)

### Java內存模型(JMM)兩條規定
* 1.線程對共享變量的所有操作都必須在自己的工作內存中進行,不能直接從主內存中讀取
* 2.不同線程之間無法直接訪問其他線程工作內存中的變量,線程間變量值的傳遞需要通過主內存來完成
### JMM中共享變量可見性實現的原理
線程1對共享變量的修改要想被線程2及時看到,必須要經過如下2個步驟:
* 1.把工作內存1中更新過的共享變量刷新到主內存中
* 2.將主內存中最新的共享變量的值更新到工作內存2中
### 要實現共享變量的可見性,必須保證兩點
* 1.線程修改后的共享變量值能夠及時從工作內存中刷新到主內存中
* 2.其他線程能夠及時把共享變量的最新值從主內存更新到自己的工作內存中
## synchronized實現可見性
synchronized能夠實現:
1.原子性(同步)
2.可見性
### JMM關于synchronized的兩條規定
* 1.線程解鎖前,必須把共享變量的最新值刷新到主內存中
* 2.線程加鎖時,將清空工作內存中共享變量的值,從而使用共享變量時需要從主存中重新讀取最新的值(注意:加鎖與解鎖需要是同一把鎖)
線程解鎖前對共享變量的修改在下次加鎖時對其他線程可見
### 線程執行互斥代碼的過程
* 1.獲得互斥鎖
* 2.清空工作內存
* 3.從主內存拷貝變量的最新副本到工作內存
* 4.執行代碼
* 5.將更改后的共享變量的值刷新到主內存中
* 6.釋放互斥鎖
重排序的介紹:代碼書寫的順序與實際執行的順序不同,指令重排序是編譯器或處理器為了提高程序性能而做的優化
1.編譯器優化的重排序(編譯器優化)
2.指令級并行重排序(處理器優化)
3.內存系統的重排序(處理器優化)
as-if-serial的介紹:無論如何重排序,程序執行結果應與代碼順序執行結果一致(Java編譯器,運行時和處理器都會保證Java在單線程下遵循as-if-serial語義)
```
int num1 = 1; //第1行代碼
int num2 = 2; //第2行
int sum = num1 + num2; //第3行
```
單線程:第1,2行的順序可以重排,但第3行不能,重排序不會給單線程帶來內存可見性問題,多線程中程序交錯執行時,重排序可能會造成內存可見性問題
### 導致共享變量在線程間不可見的原因及synchronized解決方案
* 1.線程的交叉執行——>原子性(synchronized的同步性)
* 2.重排序結合線程交叉執行——>原子性
* 3.共享變量更新后的值沒有在工作內存與主內存間及時更新——>可見性
## volatile實現可見性
volatile關鍵字:
* 1.能夠保證volatile變量的可見性
* 2.不能保證volatile變量復合操作的原子
### volatile如何實現內存的可見性
深入來說:通過加入內存屏障和禁止重排序優化來實現的
* 1.對volatile變量執行寫操作時,會在寫操作后加入一條store屏障指令
* 2.對volatile變量執行讀操作時,會在讀操作前加入一條load屏障指令
通俗地講:volatile變量在每次被線程訪問時,都強迫從主內存中重讀該變量的值,而當該變量發生變化時,又會強迫將最新的值刷新到主內存,這樣任何時刻,不同的線程總能看到該變量的最新值
### 線程讀寫volatile變量的過程的總結
線程寫volatile變量的過程
* 1.改變線程工作內存中volatile變量副本的值
* 2.將改變后的副本的值從工作內存刷新到主內存
線程讀volatile變量的過程
* 1.從主內存中讀取volatile變量的最新值到線程的工作內存中
* 2.從工作內存中讀取volatile變量的副本
### volatile不能保證volatile變量復合操作的原子性
原子性:每次只有一條線程能執行鎖內代碼
```
private int number = 0;
number++;//不是原子操作
```
* 1.讀取number的值
* 2.將number的值加1
* 3.寫入最新的number的值
```
synchronized(this) {
number++;//加入synchronized,變為原子操作
}
private volatile int number=0;//變為volatile變量,無法保證原子性
```
volatile不能保證volatile變量復合操作的原子性,解決方案如下:
//保證number自增操作的原子性:
* 1.使用synchronized關鍵字
* 2.使用ReentrantLock關鍵字(java.util.concurrent.locks包下)
* 3.使用AtomicInterger(java.util.concurrent.atomic包下)
//使用ReentrantLock鎖保證操作的原子性:
```
private Lock lock = new ReentrantLock();
lock.lock();
try{
this.number++;
}finally{
lock.unlock();//釋放鎖
}
```
### 要在多線程中安全的使用volatile變量,必須同時滿足
1.對變量的寫入操作不依賴其當前值,不滿足:number++,count=count*5等,滿足:boolean變量,記錄溫度變化的變量等
2.該變量沒有包含在具體其他變量的不變式中,不滿足:不變式low<up
## 總結
final也可以保證內存的可見性
### synchronized和volatile比較
* 1.volatile不需要加鎖,比synchronized更輕量級,不會阻塞線程
* 2.從內存可見性角度講,volatile讀相當于加鎖,volatile寫相當于解鎖
* 3.synchronized既能保證可見性,又能保證原子性,而volatile只能保證可見性,無法保證原子性
### 即使沒有使用保證可見性的操作,很多時候共享變量都依然可以在主內存和工作內存中得到及時的更新的原因
一般只有在短時間內高并發的情況下才會出現變量得不到及時更新的情況,因為CPU在執行時間時會很快的刷新緩存,所以一般情況下很難看到這種問題
### 當程序中有一個long或double類型的變量,而且該變量也沒有任何關鍵字修飾,那么此時對64位的long,double變量的讀寫就不是原子操作,即可以分解
因為java內存模型允許JVM將沒有被volatile修飾的64位數據類型的讀寫操作劃分為兩次32位的讀寫操作來進行,就可能導致多線程并發訪問該變量時,出現只讀了一半就被搶走資源進而導致數據異常的問題,解決方法是在long,double類型的變量上加入volatile關鍵字
不過大部分虛擬機已經把long,double類型的變量對其進行一些保護,因此在實際編程中也不需刻意為long,double類型的數據加上volatile關鍵字
- java
- 設計模式
- 設計模式總覽
- 設計原則
- 工廠方法模式
- 抽象工廠模式
- 單例模式
- 建造者模式
- 原型模式
- 適配器模式
- 裝飾者模式
- 代理模式
- 外觀模式
- 橋接模式
- 組合模式
- 享元模式
- 策略模式
- 模板方法模式
- 觀察者模式
- 迭代子模式
- 責任鏈模式
- 命令模式
- 備忘錄模式
- 狀態模式
- 訪問者模式
- 中介者模式
- 解釋器模式
- 附錄
- JVM相關
- JVM內存結構
- Java虛擬機的內存組成以及堆內存介紹
- Java堆和棧
- 附錄-數據結構的堆棧和內存分配的堆區棧區的區別
- Java內存之Java 堆
- Java內存之虛擬機和內存區域概述
- Java 內存之方法區和運行時常量池
- Java 內存之直接內存(堆外內存)
- JAVA內存模型
- Java內存模型介紹
- 內存模型如何解決緩存一致性問題
- 深入理解Java內存模型——基礎
- 深入理解Java內存模型——重排序
- 深入理解Java內存模型——順序一致性
- 深入理解Java內存模型——volatile
- 深入理解Java內存模型——鎖
- 深入理解Java內存模型——final
- 深入理解Java內存模型——總結
- 內存可見性
- JAVA對象模型
- JVM內存結構 VS Java內存模型 VS Java對象模型
- Java的對象模型
- Java的對象頭
- HotSpot虛擬機
- HotSpot虛擬機對象探秘
- 深入分析Java的編譯原理
- Java虛擬機的鎖優化技術
- 對象和數組并不是都在堆上分配內存的
- 垃圾回收
- JVM內存管理及垃圾回收
- JVM 垃圾回收器工作原理及使用實例介紹
- JVM內存回收理論與實現(對象存活的判定)
- JVM參數及調優
- CMS GC日志分析
- JVM實用參數(一)JVM類型以及編譯器模式
- JVM實用參數(二)參數分類和即時(JIT)編譯器診斷
- JVM實用參數(三)打印所有XX參數及值
- JVM實用參數(四)內存調優
- JVM實用參數(五)新生代垃圾回收
- JVM實用參數(六) 吞吐量收集器
- JVM實用參數(七)CMS收集器
- JVM實用參數(八)GC日志
- Java性能調優原則
- JVM 優化經驗總結
- 面試題整理
- 面試題1
- java日志規約
- Spring安全
- OAtuth2.0簡介
- Spring Session 簡介(一)
- Spring Session 簡介(二)
- Spring Session 簡介(三)
- Spring Security 簡介(一)
- Spring Security 簡介(二)
- Spring Security 簡介(三)
- Spring Security 簡介(四)
- Spring Security 簡介(五)
- Spring Security Oauth2 (一)
- Spring Security Oauth2 (二)
- Spring Security Oauth2 (三)
- SpringBoot
- Shiro
- Shiro和Spring Security對比
- Shiro簡介
- Session、Cookie和Cache
- Web Socket
- Spring WebFlux