談到多線程,不可避免的就會談到數據爭用的問題,而要解決數據爭用,就須要學習內存可見性相關的知識java
首先對相關的幾個名詞進行解釋:緩存
1. 什麼是「可見性」:一個線程對共享變量值的修改,可以及時被其它線程看到安全
2. 什麼是「共享變量」:若是一個變量在多個線程的工做內存中都存在副本,那麼這個變量就是這幾個線程的共享變量多線程
3. 什麼是「線程的工做內存」:要了解這個,咱們須要先了解一下java內存模型(JMM)併發
JMM描述了java程序中各類變量(線程共享變量)的訪問規則,以及在JVM 中將變量存儲到內存和從內存中取出變量這樣的底層細節。ide
- 全部變量都存儲在主內存中
- 每一個線程都有本身獨立的工做內存,裏面保存該線程使用到的變量的副本(主內存中該變量的一份拷貝)
這裏有兩條規定:性能
- 線程對共享變量的全部操做都必須在本身的工做內存中進行,不能直接從主內存中讀寫
- 不一樣線程之間沒法直接訪問其餘線程工做內存中的變量,線程間變量值的傳遞須要經過主內存來完成(主內存做爲橋樑)
致使共享變量在線程間不可見的緣由:學習
所以,要實現共享變量的可見性,必須保證兩點:優化
synchronized是java語言層面支持的可見性實現方式之一(不包括jdk1.5以後,concurrent併發包下的一些高級特性),它能夠實現互斥鎖(即實現同步),從而保證在任意一個時刻都只有一個線程在執行鎖裏的代碼。線程執行互斥代碼的過程以下:this
package mkw.demo.syn; public class SynchronizedDemo { //共享變量 private boolean ready = false; private int result = 0; private int number = 1; //寫操做 public synchronized void write(){ ready = true; //1.1 number = 2; //1.2 } //讀操做 public synchronized void read(){ if(ready){ //2.1 result = number*3; //2.2 } System.out.println("result的值爲:" + result); } //內部線程類 private class ReadWriteThread extends Thread { //根據構造方法中傳入的flag參數,肯定線程執行讀操做仍是寫操做 private boolean flag; public ReadWriteThread(boolean flag){ this.flag = flag; } @Override public void run() { if(flag){ //構造方法中傳入true,執行寫操做 write(); }else{ //構造方法中傳入false,執行讀操做 read(); } } } public static void main(String[] args) { SynchronizedDemo synDemo = new SynchronizedDemo(); //啓動線程執行寫操做 synDemo .new ReadWriteThread(true).start(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //啓動線程執行讀操做 synDemo.new ReadWriteThread(false).start(); } }
深刻來講:經過加入內存屏障和禁止重排序優化來實現的。
通俗地講:volatile變量在每次被線程訪問時,都強制從主內存中讀取該變量的值,而當該變量發生變化時,又會強制線程將最新的值刷新到主內存。這樣任什麼時候刻,不一樣線程總能看到該變量的最新值。
要保證操做的原子性,有如下解決方案:
- 使用synchronized關鍵字
2. 使用ReentrantLock(java.util.concurrent.locks包下)
3. 使用AutomicInteger(java.util.concurrent.automic包下)
因爲volatile不能保證原子性,線程可能發生交叉執行,因此可能會產生一些問題:
要在多線程中安全的使用volatile變量,必須同時知足如下條件:
1. 對變量的寫入操做不依賴當前值
2. 該變量沒有包含在具備其餘變量的不變式中(若程序中有多個volatile變量,每一個volatile變量的狀態要獨立於其餘volatile變量)
寫代碼時會發現,不少時候至少都會和上述兩個條件之一衝突,所以volatile並無synchronized使用普遍
1. volatile不須要加鎖,比synchronized更輕量級,不會阻塞線程,;
2. 從內存可見性角度,volatile讀至關於加鎖,volatile寫至關於解鎖;
3. synchronized既能保證可見性,又能保證原子性,而volatile只能保證可見性,沒法保證原子性。
volatile使用有諸多限制,所以沒有synchronized使用普遍,但因爲volatile比synchronized更輕量,執行效率更高,所以若是能保證線程安全的狀況下,儘量選擇volatile