1. 程式人生 > >java的多執行緒學習,第三記

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;


}