線程八大基礎核心八(線程安全)


1.引子

在java多線程並發編程中,有八大基礎核心。
看看都有哪八大基礎核心呢?它們分別是:
    1.創建線程的方式
    2.線程啟動
    3.線程停止
    4.線程生命周期
    5.線程相關的方法
    6.線程相關的屬性
    7.線程異常處理
    8.線程安全

今天我們從第八個基礎核心開始:線程安全

2.考考你

#前情回顧 1.多線程編程中,比較為難,又需要重點關注的一個話題,就是線程安全 2.需要從理論、和實戰多個角度去看 3.本着一篇文章,信息量不要太多的原則 4.本篇文章僅相對全面的梳理線程安全的基礎 5.更多內容,結合JUC的內容,推薦了解的內容有: 線程池、鎖、CAS、ThreadLocal 並發集合、並發流程控制、AQS #考考你 1.你知道多線程的理論基礎有哪些嗎? 2.你知道線程的實現方式有哪些嗎? 3.你知道多線程安全的三要素嗎? 4.你知道java的內存模型JMM嗎? 5.你知道讓線程安全的常規手段嗎? 6.你知道java中的volatile關鍵字嗎?

3.案例

3.1.困惑的i++操作

簡述:

1.在我們的日常開發中,經常會寫:i++這樣的操作

2.問題:那么它到底是不是線程安全的呢?

3.關鍵點:問題的關鍵在於i++是不是原子性操作。即i++對於操作系統,或者說對於jvm執行子系統,是一條指令,還是多條指令?

3.1.1.案例代碼

package com.anan.thread.threadsafe;

/**
 * 讓人困惑的i++操作
 */
public class ThreadSafeIAddOper {

    // 定義自增操作變量:i
    public  static int i_add = 0;

    // 在方法中,進行i_add的自增操作
    public  static void addI(){
        i_add++;
    }

    public static void main(String[] args) {
        // 創建20個線程,並行執行i_add自增操作
        Runnable r1 = new MyRunnable();

        // for循環,創建20個線程
        for (int i = 0; i < 20; i++) {
            new Thread(r1).start();
        }

        // 等待20個子線程執行結束后,主線程main輸出i_add的值
        while(Thread.activeCount() > 2){
            ;
        }
        System.out.println("i_add變量最終值:" +i_add);
    }

    /**
     * 實現Runnable接口,創建線程
     */
    static class MyRunnable implements Runnable{
        public void run() {
            // for循環,執行i_add自增操作:10000次
            for (int i = 0; i < 10000; i++) {
                addI();
            }
        }
    }
}

3.1.2.執行結果

 

3.1.3.ThreadSafeAddOper字節碼文件內容

簡述:

1.彩蛋:通過javap工具,查看字節碼文件結構

2.說明i++操作,對於jvm執行子系統,不是原子性(是由多條指令組成)

3.以下是類:ThreadSafeIAddOper,對應的class文件內容

D:\03other\02study\coding\mypro\thread-pro\target\classes>javap -v com.anan.thread.threadsafe.ThreadSafeIAddOper
Classfile /D:/03other/02study/coding/mypro/thread-pro/target/classes/com/anan/thread/threadsafe/ThreadSafeIAddOper.class
  Last modified 2020-2-15; size 1259 bytes
  MD5 checksum 6b289d7c5da1749f03e41da116a3b9a6
  Compiled from "ThreadSafeIAddOper.java"
public class com.anan.thread.threadsafe.ThreadSafeIAddOper
  minor version: 0
  major version: 49
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
 ......內容省略......

  public static void addI();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #10                 // Field i_add:I
         3: iconst_1
         4: iadd
         5: putstatic     #10                 // Field i_add:I
         8: return
      LineNumberTable:
        line 13: 0
        line 14: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

  public static void main(java.lang.String[]);
    .......內容省略......

D:\03other\02study\coding\mypro\thread-pro\target\classes>

3.1.4.i++對應的字節碼指令說明

簡述:

1.通過截圖,可以看到一個i++操作,在字節碼層面,對應了四條jvm字節碼指令:

getstatic、iconst_1、iadd、putstatic

2.說明對於jvm來說,i++不是原子性操作

 

3.2.線程安全基本手段:鎖

簡述:

1.改造3.1案例代碼,通過加鎖實現:多條指令操作的原子性。從而實現線程安全。

2.給addI方法,增加synchronized同步鎖

 

執行結果:

 

 

3.3.關鍵字volatile錯誤使用案例

簡述:

改造3.1.案例代碼,通過volatile關鍵字修飾:

1.說明volatile關鍵字,只能保障線程的可見性(即一個線程修改了volatile關鍵字修改的變量后,會立即刷新到主內存,讓其它線程可見)。

2.但volatile關鍵字,不能保障原子性,對於i++操作,它還是不能保障線程安全

3.關於volatile關鍵字的正確使用方式,請看討論分享中內容說明。

 

執行結果:

 

 

4.討論分享

#考考你答案 1.你知道多線程的理論基礎有哪些嗎? 1.1.進程與線程的區別 1.2.線程實現方式 1.3.線程安全三要素 1.4.java內存模型JMM 1.5.鎖 2.你知道進程與線程的區別嗎? 2.1.進程是操作系統【分配資源】的最小單位 2.2.線程是操作系統【調度】的最小單位 3.你知道線程的實現方式有哪些嗎? 3.1.基於操作系統內核實現方式(內核線程) 3.2.基於用戶進程實現方式(用戶態線程,即協程) 3.3.java的線程實現方式是:內核線程實現方式 4.你知道多線程安全的三要素嗎? 4.1.線程安全要素一:原子性 4.2.線程安全要素二:可見性 4.3.線程安全要素三:有序性 5.你知道java的內存模型JMM嗎? 5.1.參見附圖 6.你知道java編程中,線程安全的常規手段嗎? 6.1.線程安全常規手段一:加鎖 6.2.線程安全常規手段二:消除共享資源 7.你知道java中的volatile關鍵字嗎? 7.1.volatile關鍵字是一種輕量級線程安全實現方式 7.2.volatile關鍵字的底層原理:保證可見性,禁止重排序 7.3.使用volatile關鍵字注意事項: a.volatile關鍵字修飾變量值修改,不依賴原來的值;或者只有單一線程進行修改 b.volatile關鍵字修飾的變量,不與其它變量一起參與原子性約束 c.滿足a、b兩條,那么volatile關鍵字修飾的變量,在多線程下是線程安全的

 

 java內存模型JMM:

 (到這里,多線程基礎編程暫時告一段落,本系列是學習筆記,參考了悟空老師的課程:《Java並發核心知識體系精講》。歡迎大家去學習悟空老師的課程,講的非常好!同時向悟空老師問好!)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM