Java內存模型( JMM ) :html
1) 全部的變量都存儲在主內存中java
2) 每一個線程都有本身獨立的工做內存, 裏面保存該線程使用到的變量的副本 ( 主內存中該變量的一份拷貝 )ide
JMM 兩條規定:ui
1) 線程對共享變量的全部操做都必須在本身的工做內存中進行spa
2) 不一樣線程之間沒法直接訪問其餘線程工做內存中的共享變量, 線程間共享變量值的傳遞必須經過主內存 .net
線程間共享變量可見性實現的原理:線程
線程A 對共享變量的修改想被線程B 及時看到, 必需要通過如下2個步驟:code
1) 把線程A 工做內存中更新過的共享變量刷新到主內存中( store )htm
2) 將主內存中最新的共享變量的值共享到線程B 工做內存中( load )blog
Java 語言層面支持的可見性實現方式:
1) synchronized
2) volatile
JUC 包下的類也能夠實現可見性
1) Atomic
2) ReentrantLock
3) Semaphore
1. synchronized 實現可見性
JMM 關於 synchronized 的兩條規定:
1) 線程釋放鎖前, 必須把共享變量的最新值從該線程的工做內存刷新到主內存中
2) 線程持有鎖時, 將清空該線程工做內存中共享變量的值, 從主內存中讀取最新的值
synchronized 實現可見性的緣由:
線程釋放鎖前對共享變量的修改在下次持有鎖時對其餘線程可見
public class SynchronizedDemo { // 共享變量 private int result = 0; // 共享變量執行自增操做 public synchronized void increase() { result++; } public int getResult() { return result; } public static void main(String[] args) throws InterruptedException { final SynchronizedDemo demo = new SynchronizedDemo(); // 設置啓動500個線程 int count = 500; // 建立一個 JUC 包下的同步同步計數器, 設置計數次數爲線程數500 final CountDownLatch cdl = new CountDownLatch(count); for (int i = 0; i < count; i++) { new Thread(new Runnable() { @Override public void run() { demo.increase(); cdl.countDown(); } }).start(); } cdl.await(); System.out.println(demo.getResult()); } }
Console 輸出: 500
synchronized 不只能夠實現可見性, 還能夠實現原子性
volatile 如何實現可見性:
經過加入內存屏障和禁止指令重排序
1) 對 volatile 變量執行寫操做時, 會在寫操做後加入一條 store 屏障指令
2) 對 volatile 變量執行讀操做時, 會在讀操做前加入一條 load 屏障指令
public class VolatileDemo { // 使用 volatile 修飾共享變量 private volatile int result = 0; // 共享變量 result 執行自增操做, 沒法保證原子性 public void increase() { result++; } public int getResult() { return result; } public static void main(String[] args) throws InterruptedException { final VolatileDemo demo = new VolatileDemo(); // 設置啓動500個線程 int count = 500; // 建立一個 JUC 包下的同步同步計數器, 設置計數次數爲線程數500 final CountDownLatch cdl = new CountDownLatch(count); for (int i = 0; i < count; i++) { new Thread(new Runnable() { @Override public void run() { demo.increase(); cdl.countDown(); } }).start(); } cdl.await(); System.out.println(demo.getResult()); } }
Console 輸出: 498
volatile 關鍵字, 能保證 volatile 變量的可見性, 不能保證 volatile 變量操做的原子性( 如 ++/-- )
3. AtomicInteger 實現可見性
用 Atomic 類實現共享變量在線程中的原子性( 經過 CAS, 自旋 實現)
public class AtomicIntegerDemo { // 共享變量 private AtomicInteger result = new AtomicInteger(0); // 使用 Atomic 類的 incrementAndGet() 方法( 原子操做 ), 實現自增 public void increase() { result.incrementAndGet(); } public int getResult() { return result.get(); } public static void main(String[] args) throws InterruptedException { final AtomicIntegerDemo demo = new AtomicIntegerDemo(); // 設置啓動500個線程 int count = 500; // 建立一個JUC包下的同步同步計數器, 設置計數次數爲線程數500 final CountDownLatch cdl = new CountDownLatch(count); for (int i = 0; i < count; i++) { new Thread(new Runnable() { @Override public void run() { demo.increase(); cdl.countDown(); } }).start(); } cdl.await(); System.out.println(demo.getResult()); } }
Console 輸出: 500
4. JUC 包下的 Lock 實現可見性
用 ReentrantLock 實現共享變量在線程中的原子性
public class LockDemo { // 共享變量 private int result = 0; // 可重入鎖 private Lock lock = new ReentrantLock(); // 使用鎖機制, 保證鎖內代碼的原子性 public void increase() { // 加鎖 lock.lock(); try { result++; } finally { // 釋放鎖 lock.unlock(); } } public int getResult() { return result; } public static void main(String[] args) throws InterruptedException { final LockDemo demo = new LockDemo(); // 設置啓動500個線程 int count = 500; // 建立一個JUC包下的同步同步計數器, 設置計數次數爲線程數500 final CountDownLatch cdl = new CountDownLatch(count); for (int i = 0; i < count; i++) { new Thread(new Runnable() { @Override public void run() { demo.increase(); cdl.countDown(); } }).start(); } cdl.await(); System.out.println(demo.getResult()); } }
Console 輸出: 500
5. Semaphore 實現可見性
用信號量機制實現共享變量在線程中的原子性
public class SemaphoreDemo { // 共享變量 private int result = 0; // 初始化信號量爲1, 一次只能有1個線程訪問共享變量, 至關於互斥鎖 private Semaphore semaphore = new Semaphore(1); // 使用信號量機制, 保證共享變量自增操做的原子性 public void increase() { try { // 獲取1個信號量 semaphore.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } result++; // 釋放1個信號量 semaphore.release(); } public int getResult() { return result; } public static void main(String[] args) throws InterruptedException { final SemaphoreDemo demo = new SemaphoreDemo(); // 設置啓動500個線程 int count = 500; // 建立一個JUC包下的同步同步計數器, 設置計數次數爲線程數500 final CountDownLatch cdl = new CountDownLatch(count); for (int i = 0; i < count; i++) { new Thread(new Runnable() { @Override public void run() { demo.increase(); cdl.countDown(); } }).start(); } cdl.await(); System.out.println(demo.getResult()); } }
Console 輸出: 500
總結:
synchronized 代碼塊具有 可見性 和 可見性
volatile 變量具有可見性, 不具有原子性