欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java的多線程高并發(fā)詳解

 更新時(shí)間:2021年04月16日 15:05:51   作者:清云青云  
這篇文章主要介紹了java的多線程高并發(fā)詳解,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java的小伙伴們有很好地幫助,需要的朋友可以參考下

1.JMM數(shù)據(jù)原子操作

  • read(讀取)∶從主內(nèi)存讀取數(shù)據(jù)
  • load(載入):將主內(nèi)存讀取到的數(shù)據(jù)寫入工作內(nèi)存
  • use(使用):從工作內(nèi)存讀取數(shù)據(jù)來計(jì)算
  • assign(賦值):將計(jì)算好的值重新賦值到工作內(nèi)存中
  • store(存儲):將工作內(nèi)存數(shù)據(jù)寫入主內(nèi)存
  • write(寫入):將store過去的變量值賦值給主內(nèi)存中的變量
  • lock(鎖定):將主內(nèi)存變量加鎖,標(biāo)識為線程獨(dú)占狀態(tài)
  • unlock(解鎖):將主內(nèi)存變量解鎖,解鎖后其他線程可以鎖定該變量

2.來看volatile關(guān)鍵字

(1)啟動兩個(gè)線程

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

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

(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循環(huán)了");
        }).start();
 
        Thread.sleep(2000);
        new Thread(() -> changeFlage()).start();
    }
 
    private static void changeFlage() {
        System.out.println("開始改變flag值之前");
        flag = true;
        System.out.println("改變flag值之后");
    }
}

while語句能夠滿足條件

(3)原理解釋:

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

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

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

3.并發(fā)編程三大特性

可見性,有序性、原子性

4.雙鎖判斷機(jī)制創(chuàng)建單例模式

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());
 
    }
 
}

當(dāng)線程調(diào)用getInstance方法創(chuàng)建的時(shí)候,先判斷是否為空,為空則把對象加上鎖,否則多線程的情況會創(chuàng)建重復(fù),再鎖里面再次判斷是否為空,當(dāng)new一個(gè)對象的時(shí)候,先在內(nèi)存分配空間,再執(zhí)行對象的init屬性賦零操作,再執(zhí)行初始化賦值操作。

cpu為了優(yōu)化代碼執(zhí)行效率,會對滿足as-if-serial和happens-before原則的代碼進(jìn)行指令重排序,as-if-serial規(guī)定線程內(nèi)的執(zhí)行代碼順序不影響結(jié)果輸出,則會進(jìn)行指令重排;

happens-before規(guī)定一些鎖的順序,同一個(gè)對象的unlock需要出現(xiàn)下一個(gè)lock之前等。

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

5.synchronized關(guān)鍵字

(1)沒加synchronized之前

package com.qingyun;
 
/**
 * Synchronized關(guān)鍵字
 */
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;
    }
}

輸出結(jié)果不是我們想要的,由于線程和for循環(huán)同時(shí)去調(diào)加的方法,導(dǎo)致最后輸出的結(jié)果不是我們想要的

(2)加上synchronized之后

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

輸出的結(jié)果是我們想要的,synchronized關(guān)鍵字底層使用的lock,是重量級鎖,互斥鎖、悲觀鎖,jdk1.6之前的鎖,線程會放到一個(gè)隊(duì)列里面等待著執(zhí)行

6.AtomicIntger原子操作

(1)給原子加1的操作,可以使用AtomicInteger實(shí)現(xiàn),與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源碼有一個(gè)value字段,使用volatile修飾,volatile底層使用lock修飾,保證多線程并發(fā)結(jié)果的正確

private volatile int value;

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

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

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

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

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

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

7.鎖優(yōu)化

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

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

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

(4)分段CAS,底層有一個(gè)base記錄變量值,當(dāng)有多個(gè)線程類訪問此變量是,base的值會分為多個(gè)cell,組成數(shù)組,每個(gè)cell對應(yīng)一到多個(gè)線程的cas處理,避免了線程的自旋空轉(zhuǎn),這樣還是輕量級鎖,返回?cái)?shù)據(jù)的時(shí)候,底層調(diào)用的是所有cell數(shù)組和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;
    }

到此這篇關(guān)于java的多線程與高并發(fā)詳解的文章就介紹到這了,更多相關(guān)java多線程與高并發(fā)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Maven構(gòu)建生命周期詳細(xì)介紹

    Maven構(gòu)建生命周期詳細(xì)介紹

    這篇文章主要介紹了Maven構(gòu)建生命周期詳細(xì)介紹,小編覺得還是挺不錯(cuò)的,這里分享給大家,需要的朋友可以參考下。
    2017-11-11
  • Java輸入流Scanner/BufferedReader使用方法示例

    Java輸入流Scanner/BufferedReader使用方法示例

    這篇文章主要介紹了Java輸入流Scanner/BufferedReader使用方法,大家看示例吧
    2013-11-11
  • Java selenium上傳文件的實(shí)現(xiàn)

    Java selenium上傳文件的實(shí)現(xiàn)

    本文主要介紹了Java selenium上傳文件的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • java學(xué)習(xí)之jar包的下載和導(dǎo)入

    java學(xué)習(xí)之jar包的下載和導(dǎo)入

    我們經(jīng)常碰到有些jar包在中央倉庫沒有的情況,這時(shí)候我們需要導(dǎo)入,這篇文章主要給大家介紹了關(guān)于java學(xué)習(xí)之jar包的下載和導(dǎo)入的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • Springboot打印接口的三種方式分享

    Springboot打印接口的三種方式分享

    這篇文章主要為大家詳細(xì)介紹了Springboot打印接口的三種方式:aop切面的方式、過濾器的方式和攔截器的方式,感興趣的可以跟隨小編一起學(xué)習(xí)一下
    2022-08-08
  • 詳解java如何集成swagger組件

    詳解java如何集成swagger組件

    今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識,文章圍繞著java如何集成swagger組件展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • IDEA啟動tomcat狀態(tài)404的解決

    IDEA啟動tomcat狀態(tài)404的解決

    在使用Idea進(jìn)行Java?Web開發(fā)過程中,經(jīng)常會遇到Tomcat出現(xiàn)404錯(cuò)誤的問題,本文就來介紹了IDEA啟動tomcat狀態(tài)404的解決,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • springboot項(xiàng)目中mybatis-plus@Mapper注入失敗問題

    springboot項(xiàng)目中mybatis-plus@Mapper注入失敗問題

    這篇文章主要介紹了springboot項(xiàng)目中mybatis-plus@Mapper注入失敗問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • springboot實(shí)現(xiàn)公眾號接收回復(fù)消息和超過5秒被動回復(fù)消息

    springboot實(shí)現(xiàn)公眾號接收回復(fù)消息和超過5秒被動回復(fù)消息

    本文主要介紹了springboot實(shí)現(xiàn)公眾號接收回復(fù)消息和超過5秒被動回復(fù)消息,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Javaweb中使用Jdom解析xml的方法

    Javaweb中使用Jdom解析xml的方法

    Jdom是一個(gè)開源項(xiàng)目,基于樹形結(jié)構(gòu),利用純java的技術(shù)對XML文檔實(shí)現(xiàn)解析,生成,序列化以及多種操作.這篇文章主要介紹了Javaweb中使用Jdom解析xml的方法的相關(guān)資料,需要的朋友可以參考下
    2016-09-09

最新評論