• 多线程基础原理篇


    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

    前言

    java多线程并发编程本质上就是为了充分使用CPU从而提高程序执行效率。为了避免多线程并发环境下数据一致性问题,并根据并发编程特性提供一系列实现解决这个问题。
    并发三大特性:原子性、可见性、有序性;锁概念和锁实现;volatile和synchronization;重量锁和轻量锁以及锁升级;CAS和AQS。
    sunchronized锁升级流程图、CAS流程图、AQS流程图。


    一、进程和线程

    在这里插入图片描述

    1. 进程

    进程是操作系统资源分配的基本单位。
    例如在操作系统中,存在各种可执行的程序文件,当我们启动程序时,操作系统会在内存中为程序运行分配内存空间,这个正在执行的程序就是一个进程。一个操作系统可以同时启动多个程序,也就是可以同时运行多个进程。

    2. 线程

    线程是操作系统调度单位的最小单位。
    在一个进程中可以创建多条线程,例如一个可以同时执行多个任务。多条线程共享同一进程下的资源(这就是为什么会有多线程并发问题)。

    3. 串行、并行和并发

    在这里插入图片描述

    串行:同一时间只能做一件事情,需要等前面事情处理完了再执行下一个。
    并行:同一时间多个事务同时进行。
    并发:在程序执行中,多个事务交替执行。
    注:

    1. 并发的出现是为了提高CPU的利用率。因为CPU的执行效率对比于IO要快得多,大多数情况下CPU在执行完计算后需要等待IO,并发的出现可以让CPU不需要等待IO而是切换线程执行其它任务。
    2. 并发是伪并行:因为CPU的执行速度和切换时间非常短,可以看作在同一时间执行多个任务。

    总结

    java多线程并发编程本质上就是为了充分使用CPU从而提高程序执行效率。为了避免多线程并发环境下数据一致性问题,并根据并发编程特性提供一系列实现解决这个问题。

    二、java线程创建方式和区别

    1.四种方式

    a. 继承Thread类,重写run()方法后调用start()方法开启线程。
    b. 实现Runnable接口,实现run()方法,新建一个Thread将Runnable作为构造参数传入比调用start()方法启动线程。
    c. 实现Callable接口,实现call()方法。
    d. 使用线程池方式获取线程对象。

    2. 区别

    Thread和Runnable的区别:一个是继承,一个是接口,从对象特性看使用接口实现的更为灵活。
    Runnable和Callable的区别:call()方法有返回值,run()方法没有返回值。

    3. java中线程的几种状态

    在操作系统中,线程只有运行、阻塞和结束三个状态。在java环境中,java为线程管理又多了新建、就绪、wait、sleep、block等状态。
    a. 新建:new Thread()。
    b. 就绪:调用 start()方法。
    c. 运行:线程获取到cpu资源执行。
    d. 阻塞:cpu线程切换、锁等待、线程调用wait()sleep()方法。
    e. 结束:线程执行完毕或者调用interrupt()stop()等方法。

    三、并发编程的三大特性

    (一) 有序性

    理解并发编程的有序性,需要先理解Happens-Before原则和指令重排。

    1. jvm的Happens-Before

    在java环境中,需要一个规则来确定操作与操作之间的先后执行顺序,这个判断规则就是Happens-Before规则。分别有以下几条规则:

    1. 程序顺序规则:在一个线程中,每个操作Happens-Before与后面操作。(注:这里所谓的操作顺序与指令重排优化并不冲突,这里限制的其实是之前面操作执行结果对于后面操作必须可见,如果不满足这个条件则不允许重排)
    2. volatile变量规则:如果变量声明了volatile,则对这个变量的写Happens-Before与所有对变量的读操作。(注:这里的读操作包括多线程环境以及指令重排条件)
    3. 锁规则:对一个锁的解锁Happens-Before于对这个锁的加锁操作。(也就是说想要获取这个锁,必须等待这个锁释放,也可以理解为添加了锁功能的操作必须等获得锁才能继续执行)
    4. 传递性:如果A操作Happens-Before于B操作,B操作Happens-Before于C操作,则A操作Happens-Before于C操作。
    5. 线程启动规则:Thread对象的start()方法Happens-Before于此线程其它动作。
    6. 线程终止规则:线程的所有操作都Happens-Before于线程的终止检测。(也就是如果一个线程查询状态为结束,表示该线程的所有操作都以及执行)
    7. 线程中断规则:线程的interrupt()方法Happens-Before线程中断执行。(也就是说如果线程发生中断,会在中断执行之前执行interrupt方法)

    2. 指令重排

    java属于高级语言,在jvm中会进一步解析编译为操作系统可执行的编程语言。在java语言中一行代码会解析为多条指令,同时jvm为了提高执行效率,会在符合Happens-Before原则下对这些指令进行重排序。
    例如:java的半初始化状态,会对成员变量的赋值拆分为三步:开辟内存空间并设置默认值、对内存空间赋值引用、对内存空间设置真实值。

    (二)可见性

    先了解线程不可见性再理解为什么会需要可见性。如图,当线程操作共享资源时,会先从共享内存空间拷贝数据到线程临时空间,这个临时空间是线程独享的,其它线程不可见。同时,每个线程内部的操作过程对另一个线程来说也是不可见的。
    在这里插入图片描述
    由于线程直接的不可见性,为了保证多线程并发环境下数据安全,需要保证共享数据在多线程操作中的可见性。
    在java中,提供了volatile和synchronized保证数据可见性。

    (三)原子性

    在java环境中,使用了锁来实现原子性。也就是加了锁的操作(属性赋值、代码块)会作为一个整体执行。

    (四)具体案例

    1. 从对象和变量的半初始状态分析指令重排和线程安全。
    2. 从双重检查单例模式分析volatile和synchronized

    四、java的锁概念和实现

    (一)JVM内存屏障

    JVM提供了四个汇编指令级别内存屏障来保证内存数据读写的原子性。
    LoadLoad、LoadStore、StoreStore、StoreLoad

    (二)volatile 和 synchronized

    使用volatile修饰的变量保证了线程可见性和禁止指令重排。
    java提供synchronizated实现对方法或者代码块的加锁动作。

    (三)锁的分类

    重量级锁:早期JDK版本的synchronized
    轻量级锁:偏向锁、自旋锁

    (四)synchronized锁升级

    如图,java的synchronized升级经历了偏向锁-轻量级锁(自旋锁)-重量级锁的过程。
    在这里插入图片描述

    (五)CAS

    CAS 是单词 complete and save缩写,是乐观锁的一种实现。具体流程图如下,
    在这里插入图片描述

    实现CAS有两点需要注意:

    1. 在进行判断和更新内存这两个动作需要保证原子性。
    2. 为了避免ABA问题,可以增加一个version属性判断。
    3. 判断不相等时循环操作直到成功可以看成自旋锁的实现。

    (六)AQS

    AQS是AbstractQueueSynchronizer缩写。AQS底层就是volatile和CAS的实现。

    1. AQS数据结构

    如下图,AQS主要内存结构:
    在这里插入图片描述

    1. 声明volatile的state变量,可以看成是一个锁对象,进入线程操作前需要竞争获得state。
    2. 一个存储类型为thread的双向队列,双向是为了方便实现入队出队操作。
    3. 一个指向队头的head变量。
    4. 一个指向队尾的tail变量。

    2. AQS的执行顺序

    在这里插入图片描述

    1. 代码执行流程:acquire-tryAcquire-addWaiter-acquireQuered
    2. 两个CAS方法:completeAndSetState()和aompleteAndSetTail()
    3. acquireQueued方法是一个自旋锁实现。
  • 相关阅读:
    咚咚咚,你的王国之泪已上线「GitHub 热点速览」
    如何定义需求优先级?
    计算机毕业设计springboot+vue基本微信小程序的家装公司管理系统小程序
    设计模式(十)—— 外观模式
    JavaScript在IE和标准浏览器下的兼容性处理
    110-注解JSONField、DateTimeFormat、JsonFormat、JsonProperty
    NFTScan NFT API 在 NFTFi 开发中的应用
    神经滤镜为什么不能用,ps神经网络滤镜安装包
    22-9-16学习笔记
    yolov5运行过程遇到的小问题(随时更新)
  • 原文地址:https://blog.csdn.net/chenhaotao/article/details/125621484