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++操做
簡述:html
1.在咱們的平常開發中,常常會寫:i++這樣的操做java
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字節碼文件內容
簡述:bash
1.彩蛋:經過javap工具,查看字節碼文件結構多線程
2.說明i++操做,對於jvm執行子系統,不是原子性(是由多條指令組成)併發
3.如下是類:ThreadSafeIAddOper,對應的class文件內容jvm
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_一、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併發核心知識體系精講》。歡迎你們去學習悟空老師的課程,講的很是好!同時向悟空老師問好!)