亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在java中實現多線程高并發

發布時間:2021-04-17 15:41:51 來源:億速云 閱讀:174 作者:Leah 欄目:開發技術

這篇文章將為大家詳細講解有關怎么在java中實現多線程高并發,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

1.JMM數據原子操作

  • read(讀取)∶從主內存讀取數據

  • load(載入):將主內存讀取到的數據寫入工作內存

  • use(使用):從工作內存讀取數據來計算

  • assign(賦值):將計算好的值重新賦值到工作內存中

  • store(存儲):將工作內存數據寫入主內存

  • write(寫入):將store過去的變量值賦值給主內存中的變量

  • lock(鎖定):將主內存變量加鎖,標識為線程獨占狀態

  • unlock(解鎖):將主內存變量解鎖,解鎖后其他線程可以鎖定該變量

2.來看volatile關鍵字

(1)啟動兩個線程

public class VolatileDemo {
 
    private static boolean flag = false;
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            while (!flag){
            }
            System.out.println("跳出while循環了");
        }).start();
 
        Thread.sleep(2000);
        new Thread(() -> changeFlage()).start();
    }
 
    private static void changeFlage() {
        System.out.println("開始改變flag值之前");
        flag = true;
        System.out.println("改變flag值之后");
    }
}

沒加volatile之前,第一個線程的while判斷一直滿足

怎么在java中實現多線程高并發

(2)給變量flag加了volatile之后

public class VolatileDemo {
 
    private static volatile boolean flag = false;
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            while (!flag){
            }
            System.out.println("跳出while循環了");
        }).start();
 
        Thread.sleep(2000);
        new Thread(() -> changeFlage()).start();
    }
 
    private static void changeFlage() {
        System.out.println("開始改變flag值之前");
        flag = true;
        System.out.println("改變flag值之后");
    }
}

while語句能夠滿足條件

怎么在java中實現多線程高并發

(3)原理解釋:

開啟第一個線程時,flag變量通過read從主內存中讀出數據,使用load把數據加載進線程一的工作內存,通過use把flag讀取到線程中;線程二也是同樣的讀取操作。線程二通過assign改變了flag的值,線程二工作內存中存儲的flag=true,再通過store把flag寫入到總線,總線再把flag通過write寫入到住內存;由于兩個線程讀取操作的都是各種工作內存中的值,是主內存的副本,相互不通信,所以線程一一直再循環,線程一的flag為false。

加了volatile后,添加了緩存一致性協議(MESI),CPU通過總線嗅探機制感知到數據的變化而自己緩存里的值失效,此時線程一會把工作內存中存放的flag失效,從主內存中重新讀取flag的值,此時滿足while條件。

volatile底層通過匯編語言的lock修飾,當變量有修改立馬寫回主類,避免指令重排序

怎么在java中實現多線程高并發

3.并發編程三大特性

可見性,有序性、原子性

4.雙鎖判斷機制創建單例模式

public class DoubleCheckLockSinglenon {
 
    private static volatile DoubleCheckLockSinglenon doubleCheckLockSingleon = null;
 
    public DoubleCheckLockSinglenon(){}
 
 
 
    public static DoubleCheckLockSinglenon getInstance(){
        if (null == doubleCheckLockSingleon) {
            synchronized(DoubleCheckLockSinglenon.class){
                if(null == doubleCheckLockSingleon){
                    doubleCheckLockSingleon = new DoubleCheckLockSinglenon();
                }
            }
        }
        return doubleCheckLockSingleon;
    }
 
 
    public static void main(String[] args) {
        System.out.println(DoubleCheckLockSinglenon.getInstance());
 
    }
 
}

當線程調用getInstance方法創建的時候,先判斷是否為空,為空則把對象加上鎖,否則多線程的情況會創建重復,再鎖里面再次判斷是否為空,當new一個對象的時候,先在內存分配空間,再執行對象的init屬性賦零操作,再執行初始化賦值操作。

cpu為了優化代碼執行效率,會對滿足as-if-serial和happens-before原則的代碼進行指令重排序,as-if-serial規定線程內的執行代碼順序不影響結果輸出,則會進行指令重排;

happens-before規定一些鎖的順序,同一個對象的unlock需要出現下一個lock之前等。

所以為了防止new的時候,指令重排,先進行賦值再執行賦零操作情況,需要加上volatile修飾符,加上volatile修飾后,在new操作時會創建內存屏障,高速cpu不進行指令重排序,底層是lock關鍵字;內存屏障分為LoadLoad(讀讀)、storestore(寫寫)、loadstore(讀寫)、storeload(寫讀),底層是c++代碼寫的,c++代碼再調用匯編語言

5.synchronized關鍵字

(1)沒加synchronized之前

package com.qingyun;
 
/**
 * Synchronized關鍵字
 */
public class SynchronizedDemo {
 
    public static void main(String[] args) throws InterruptedException {
 
        Num num = new Num();
       Thread t1 = new Thread(() -> {
            for (int i = 0;i < 100000;i++) {
                num.incrent();
            }
        });
        t1.start();
 
        for (int i = 0;i < 100000;i++) {
            num.incrent();
        }
        t1.join();
        System.out.println(num.getNum());
    }
}
package com.qingyun;
 
public class Num {
 
    public int num = 0;
 
    public void incrent() {
        num++;
    }
 
    public int getNum(){
        return num;
    }
}

輸出結果不是我們想要的,由于線程和for循環同時去調加的方法,導致最后輸出的結果不是我們想要的

怎么在java中實現多線程高并發

(2)加上synchronized之后

public synchronized void incrent() {
        num++;
    }
 
//或者
 
  public  void incrent() {
        synchronized(this){
            num++;
        }
    }

怎么在java中實現多線程高并發

輸出的結果是我們想要的,synchronized關鍵字底層使用的lock,是重量級鎖,互斥鎖、悲觀鎖,jdk1.6之前的鎖,線程會放到一個隊列里面等待著執行

6.AtomicIntger原子操作

(1)給原子加1的操作,可以使用AtomicInteger實現,與synchronized相比,性能大大提升

public class Num {
 
   // public int num = 0;
    AtomicInteger atomicInteger = new AtomicInteger();
 
    public  void incrent() {
       atomicInteger.incrementAndGet(); //原子加1
    }
 
    public int getNum(){
        return atomicInteger.get();
    }
}

AtomicInteger源碼有一個value字段,使用volatile修飾,volatile底層使用lock修飾,保證多線程并發結果的正確

private volatile int value;

(2)atomicInteger.incrementAndGet()方法做的事情:先獲取到value的值,給值加1,再使用舊的值和atomicInteger進行比較,相等了把newValue設置進去,由于使用多線程可能值會不相等的情況,所以使用while進行循環比對,相等了執行完才推出

while(true) {
    int oldValue = atomicInteger.get();
    int newValue = oldValue+1;
    if(atomicInteger.compareAndSet(oldValue,newValue)){
       break;
    }
}

(3)atomicInteger.compareAndSet比對完值后才設置新值的方式即為CAS:無鎖、樂觀鎖、輕量級鎖,synchroznied存在線程阻塞、上行文切換、操作系統調度比較費時;CAS一直循環比對執行,效率要高

(4)compareAndSetInt底層使用native修飾,底層是c++代碼,實現了原子性問題,在匯編語言使用代碼lock cmpxchqq保證了原子性,是緩存行鎖

(5)ABA問題:線程一那到一個變量,線程二執行比較快,也拿到這個變量,把變量的值進行修改,再快速修改回原來的值,這樣變量的值有過一次變化,線程一再去執行compareAndSet的時候,雖然值還是之前的沒變,但是已經發生過變化了,出現ABA問題

(6)解決ABA問題就是給變量加版本,每次操作變量版本加1,JDK帶版本的鎖有AtomicStampedReference,這樣就算變量被其它線程修改過再回復原值,版本號也是不一致的。

7.鎖優化

(1)重量級鎖會把等待的線程放到隊列中,重量級鎖鎖定的是monitor,存在上下問切換的資源占用;輕量級鎖若是線程太多,會存在自旋,耗費cpu

(2)jdk1.6之后,鎖升級為無狀態-》偏向鎖(鎖id指定)-》輕量級鎖(自旋膨脹)-》重量級鎖(隊列存儲)

(3)創建一個對象,此時對象為無狀態,當啟動了一個線程時,再創建一個對象時,啟用偏向鎖,偏向鎖執行完之后不會釋放鎖;當再啟用一個線程時,有兩個線程來掙搶對象時,立馬又偏向鎖升級為輕量級鎖;當再創建一個線程的來掙搶對象鎖時,由輕量級鎖升級為重量級鎖

(4)分段CAS,底層有一個base記錄變量值,當有多個線程類訪問此變量是,base的值會分為多個cell,組成數組,每個cell對應一到多個線程的cas處理,避免了線程的自旋空轉,這樣還是輕量級鎖,返回數據的時候,底層調用的是所有cell數組和base的加和

public class Num {
 
    LongAdder longAdder = new LongAdder();
 
    public  void incrent() {
 
        longAdder.increment();
    }
 
    public long getNum(){
       return longAdder.longValue();
    }
}
public long longValue() {
        return sum();
    }
public long sum() {
        Cell[] as = cells; Cell a;
        long sum = base;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    sum += a.value;
            }
        }
        return sum;
    }

關于怎么在java中實現多線程高并發就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

茶陵县| 大渡口区| 巴楚县| 海口市| 岑巩县| 平武县| 普格县| 龙江县| 曲麻莱县| 湖南省| 鄂尔多斯市| 黄山市| 太保市| 沅江市| 黄大仙区| 临邑县| 双峰县| 泾川县| 嘉定区| 醴陵市| 新源县| 泰来县| 新余市| 民权县| 岳池县| 连州市| 苍山县| 宁乡县| 潢川县| 崇明县| 山东| 宕昌县| 广州市| 永安市| 抚顺市| 五大连池市| 图们市| 沭阳县| 华蓥市| 肥东县| 太谷县|