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

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併發核心知識體系精講》。歡迎你們去學習悟空老師的課程,講的很是好!同時向悟空老師問好!)

相關文章
相關標籤/搜索