zoukankan      html  css  js  c++  java
  • 线程八大基础核心八(线程安全)

    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:3other2studycodingmypro	hread-pro	argetclasses>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:3other2studycodingmypro	hread-pro	argetclasses>

    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并发核心知识体系精讲》。欢迎大家去学习悟空老师的课程,讲的非常好!同时向悟空老师问好!)

  • 相关阅读:
    散列
    AVL树的单旋与双旋
    Ubuntu系统目录
    os x文件系统结构简介
    c语言静态局部变量
    创建J2EE 5.0工程后,JSTL不能使用解决方法
    mysql
    指针
    servlet 访问项目
    c数组
  • 原文地址:https://www.cnblogs.com/itall/p/12320265.html
Copyright © 2011-2022 走看看