线程八大基础核心八(线程安全)

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:

猜你喜欢

转载自www.cnblogs.com/itall/p/12320265.html