java的多執行緒學習,第三記
一,Java記憶體模型
Java記憶體模型規定了所有的記憶體變數都儲存在主記憶體中。每條執行緒中還有自己的工作記憶體,執行緒的工作記憶體中儲存了被該執行緒所使用到的變數(這些變數是從主記憶體中拷貝而來)。執行緒對變數的所有操作(讀取,賦值)都必須在工作記憶體中進行。不同執行緒之間也無法直接訪問對方工作記憶體中的變數,執行緒間變數值的傳遞均通過主記憶體來完成
基於這種模型的多執行緒就會出現很多問題,多執行緒的---------髒讀資料
舉例說明:
i=123++;
現在有同時2個執行緒執行這段程式碼,加入初始值為123,那麼有兩個執行緒正常情況下得到的值,正常的值是125,
但是呢,初始時,兩個執行緒分別讀取i的值存入到各自所在的工作記憶體中,然後執行緒1進行加1操作,然後把i的最新值124寫入到記憶體。此時執行緒2的工作記憶體當中的i的值還是123,進行加1操作,i的值還是為124,然後執行緒2把i的值寫入記憶體。
最終結果i的值是124,而不是125.這就是-----------------快取一致性的問題。這種被多個執行緒訪問的變數為共享變數。
volatile 保證了變數的修改讓所有的執行緒可見 阻止指令排序 相對的來說
volatile 當一個共享變數被volatile修飾的時候呢,它會保證修改的值會立即被更新到記憶體,當有其他執行緒需要讀取時,它會去記憶體中讀取新值。
sync 能夠解決 可見性 原子性 volatile只能解決可見性 if()操作
例如,當第一個執行緒比較之後,進來修改了值,第二個比較之後,拿到的值還是原來的值,變數沒有原子化
大多數多執行緒問題都是由CAS操作引起的。compare and set 先比較,後修改。
如何解決多執行緒問題呢:
1,執行緒封閉:final 不要執行緒之間共享變數
2,堆疊 棧封閉 比如 方法內部宣告 修改 這樣不會溢位
3,ThreadLocal 執行緒繫結
Synchronized用於執行緒之間的資料共享(使變數或者程式碼塊在某一時刻只能被一個執行緒訪問),是一種以延長訪問時間來換取執行緒安全性的策略。
ThreadLocal則用於執行緒之間的資料隔離(為每一個執行緒都提供了變數的副本),是一種以空間來換取執行緒安全性的策略.
ThreadLocal的使用:
package fiveSteps; /** * @author liugang * @create 2018/12/10 23:53 **/ public class MainLocalTest { private static ThreadLocal<LocalTest> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { LocalTest local = new LocalTest(); new Thread(){ @Override public void run() { for (;;){ threadLocal.set(local); LocalTest l = threadLocal.get(); l.setNum(20); System.out.println(Thread.currentThread().getName()+"--------------"+threadLocal.get().getNum()); Thread.yield(); } } }.start(); new Thread(){ @Override public void run() { for (;;){ threadLocal.set(local); LocalTest l = threadLocal.get(); l.setNum(30); System.out.println(Thread.currentThread().getName()+"--------------"+threadLocal.get().getNum()); Thread.yield(); } } }.start(); } }
package fiveSteps; /** * @author liugang * @create 2018/12/10 23:51 **/ public class LocalTest { public int getNum() { return num; } public void setNum(int num) { this.num = num; } private int num; }