日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

并发编程之多线程线程安全(下)

發布時間:2023/12/10 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 并发编程之多线程线程安全(下) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1、什么是 Volatile?

volatile 是一個類型修飾符,具有可見性,也就是說一旦某個線程修改了該被 volatile 修飾的變量,它會保證修改的值會立即被更新到主存,當有其他線程需要讀取時,可以立即獲取修改之后的值。

在 java 中為了加快程序的運行效率,對一些變量的操作通常是在該線程的寄存器或是 CPU 緩存上進行的,之后才會同步到主存中,而加了 volatile 修飾符的變量則是直接讀寫主存。

[可以搜索了解一下 java 中的內存模型]

看下面一段代碼:

class?ThreadVolatileDemo?extends?Thread?{

????public?boolean?flag?=?true;

????@Override
????public?void?run()?{
????????System.out.println("開始執行子線程....");
????????while?(flag)?{
????????}
????????System.out.println("線程停止");
????}

????public?void?setRuning(boolean?flag)?{
????????this.flag?=?flag;
????}
}

public?class?ThreadVolatile?{
????public?static?void?main(String[]?args)?throws?InterruptedException?{
????????ThreadVolatileDemo?threadVolatileDemo?=?new?ThreadVolatileDemo();
????????threadVolatileDemo.start();
????????Thread.sleep(3000);
????????threadVolatileDemo.setRuning(false);
????????System.out.println("flag?已經設置成false");
????????Thread.sleep(1000);
????????System.out.println(threadVolatileDemo.flag);
????}
}

運行結果:

開始執行子線程....
flag?已經設置成false
false

已經將結果設置為 fasle 為什么?還一直在運行呢。

原因:線程之間是不可見的,讀取的是副本,沒有及時讀取到主內存結果。

解決辦法使用 volatile 關鍵字將解決線程之間可見性, 強制線程每次讀取該值的時候都去“主內存”中取值。

2、Volatile 與 Synchronize 的區別?

  • volatile 雖然具有可見性但是并不能保證原子性。
  • 性能方面,synchronized 關鍵字是防止多個線程同時執行一段代碼,就會影響程序執行效率,而 volatile 關鍵字在某些情況下性能要優于synchronized。但是要注意 volatile 關鍵字是無法替代 synchronized 關鍵字的,因為 volatile 關鍵字無法保證操作的原子性。
  • 上篇以及本篇多次提及原子性,借此重新了解一下多線程中的三大特性。

    原子性、可見性、有序性。

    2.1、什么是原子性?

    如果有了解過事務的小伙伴,這一塊就比較好理解了,所謂的原子性即一個或多個操作,要么全部執行完成,要么就都不執行,如果只執行了一部分,對不起,你得撤銷已經完成的操作。

    舉個例子:

    賬戶 A 向賬戶 B 轉賬 1000 元,那么必然包括 2 個操作:從賬戶 A 減去 1000,向賬戶 B 加上 1000,這兩個操作必須要具備原子性才能保證不出現一些意外的問題發生。

    總結:所謂的原子性其實就是保證數據一致、線程安全的一部分。

    2.2、什么是可見性?

    當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程就能夠立即看到修改的值。

    若兩個線程在不同的 cpu,有個變量 i ,線程 1 改變了 i 的值還沒有刷新到主存,線程 2 又使用了 i,那么這個 i 值肯定還是之前的,線程 1 對變量的修改,線程2 沒有看到,這就是可見性問題了。

    2.3、什么是有序性?

    即程序執行時按照代碼書寫的先后順序執行。在Java內存模型中,允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響到單線程程序的執行,卻會影響到多線程并發執行的正確性。(本文不對指令重排作介紹,但不代表它不重要,它是理解 java 并發原理時非常重要的一個概念)。

    重排序文章留空,后面補充。

    3、多線程之間的通訊

    什么是多線程之間的通訊?

    多線程之間通訊,其實就是多個線程在操作同一個資源,但是操作的動作不同。畫圖演示:

    多線程之間的通訊需求:

    第一個線程寫入(input)用戶,另一個線程讀取(out)用戶,實現讀一個寫一個操作。

    代碼實現:

    共享資源實習類 Res

    class?Res2{
    ????public?String?userName;
    ????public?String?userSex;
    }

    class?InputThread?extends?Thread{

    ????private?Res2?mRes;

    ????public?InputThread(Res2?mRes){
    ????????this.mRes?=?mRes;
    ????}

    ????@Override
    ????public?void?run()?{
    ????????int?count?=?0;
    ????????while?(true)?{
    ????????????synchronized?(mRes){
    ????????????????if?(count?==?0)?{
    ????????????????????mRes.userName?=?"余勝軍";
    ????????????????????mRes.userSex?=?"男";
    ????????????????}?else?{
    ????????????????????mRes.userName?=?"小紅";
    ????????????????????mRes.userSex?=?"女";
    ????????????????}
    ????????????????count?=?(count?+?1)?%?2;
    ????????????}
    ????????}
    ????}
    }

    class?OutThread?extends?Thread{

    ????private?Res2?mRes;

    ????public?OutThread(Res2?mRes){
    ????????this.mRes?=?mRes;
    ????}

    ????@Override
    ????public?void?run()?{
    ????????while?(true){
    ????????????System.out.println(mRes.userName?+?"--"?+?mRes.userSex);
    ????????}
    ????}
    }

    public?class?Demo9?{

    ????public?static?void?main(String[]?args){

    ????????Res2?res?=?new?Res2();
    ????????InputThread?intThrad?=?new?InputThread(res);
    ????????OutThread?outThread?=?new?OutThread(res);
    ????????intThrad.start();
    ????????outThread.start();

    ????}

    }

    打印結果:

    ...
    余勝軍--男
    小紅--女
    小紅--女
    余勝軍--男
    小紅--女

    在代碼中我們用到了 synchronized 關鍵字解決線程線程安全問題,所以實現了正確打印,如果不使用 synchronized 的話,可能會出現如下打印的臟數據:

    余勝軍--女
    小紅--女
    小紅--男
    余勝軍--男
    小紅--女
    wait()、 notify() 方法。

    關于該方法的介紹:

  • 因為涉及到對象鎖,他們必須都放在 synchronized 中來使用。
  • wait 必須暫停當前正在執行的線程,并釋放資源鎖,讓其他線程可以有機會運行。
  • notify/notifyall:喚醒鎖池中的線程,使之運行。
  • 了解了 wait、notify 方法后,我們來改造一下上邊的代碼:

    class?Res2{
    ????public?String?userName;
    ????public?String?userSex;
    ????public?boolean?flag?=?false;/*線程通訊標識*/
    }

    class?InputThread?extends?Thread{

    ????private?Res2?mRes;

    ????public?InputThread(Res2?mRes){
    ????????this.mRes?=?mRes;
    ????}

    ????@Override
    ????public?void?run()?{
    ????????int?count?=?0;
    ????????while?(true)?{
    ????????????synchronized?(mRes){
    ???????????????if(mRes.flag){
    ???????????????????try?{
    ???????????????????????mRes.wait();
    ???????????????????}catch?(Exception?e){

    ???????????????????}
    ???????????????}
    ???????????????if?(count?==?0)?{
    ????????????????????mRes.userName?=?"余勝軍";
    ????????????????????mRes.userSex?=?"男";
    ????????????????}?else?{
    ????????????????????mRes.userName?=?"小紅";
    ????????????????????mRes.userSex?=?"女";
    ????????????????}
    ????????????????count?=?(count?+?1)?%?2;

    ????????????????mRes.flag?=?true;
    ????????????????mRes.notify();
    ????????????}
    ????????}
    ????}
    }

    class?OutThread?extends?Thread{

    ????private?Res2?mRes;

    ????public?OutThread(Res2?mRes){
    ????????this.mRes?=?mRes;
    ????}

    ????@Override
    ????public?void?run()?{
    ????????while?(true){

    ????????????synchronized?(mRes){
    ????????????????if(!mRes.flag){

    ????????????????????try?{
    ????????????????????????mRes.wait();
    ????????????????????}?catch?(Exception?e)?{}
    ????????????????}

    ????????????????System.out.println(mRes.userName?+?"--"?+?mRes.userSex);

    ????????????????mRes.flag?=?false;
    ????????????????mRes.notify();
    ????????????}

    ????????}
    ????}
    }

    public?class?Demo9?{

    ????public?static?void?main(String[]?args){

    ????????Res2?res?=?new?Res2();
    ????????InputThread?intThrad?=?new?InputThread(res);
    ????????OutThread?outThread?=?new?OutThread(res);
    ????????intThrad.start();
    ????????outThread.start();

    ????}

    }
    wait() 與 sleep() 區別?

    sleep() 方法時屬于 Thread 類的,而 wait() 方法是屬于 Object 類的。

    sleep() 方法導致了程序暫停執行指定的時間,讓出 cpu 給其他線程,但是它的監控狀態依然保持著,當指定的時間到了又會自動恢復運行狀態。

    在調用 sleep() 方法的過程中,線程不會釋放對象鎖。

    而當調用 wait() 方法的時候,線程會放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用 notify() 方法后本線程才進入對象鎖池準備,獲取對象鎖進入運行狀態。

    Lock鎖(顯示鎖)

    lock接口提供了與 synchronized 關鍵字類似的同步功能,但需要在使用時需要手動獲取鎖和釋放鎖。

    代碼示例:

    Lock?lock??=?new?ReentrantLock();
    lock.lock();
    try{
    ????/*可能會出現線程安全的操作*/
    }finally{
    ????/*一定在finally中釋放鎖*/
    ????/*也不能把獲取鎖在try中進行,因為有可能在獲取鎖的時候拋出異常*/
    ????lock.ublock();
    }
    Condition用法—Lock中的wait()、notify()

    Condition 的功能類似于在傳統的線程技術中的 Object.wait() 和 Object.notify() 的功能。

    代碼:

    Condition?condition?=?lock.newCondition();

    res.?condition.await();??類似wait

    res.?Condition.?Signal()?類似notify

    synchronized 與 lock 的區別

    synchronized 重量級,lock 輕量級鎖,synchronized 不可控制,lock 可控制。

    lock 相對 synchronized 比較靈活。

    我創建了一個java相關的公眾號,用來記錄自己的學習之路,感興趣的小伙伴可以關注一下微信公眾號哈:niceyoo

    總結

    以上是生活随笔為你收集整理的并发编程之多线程线程安全(下)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。