02、并发编程的三大特性

并发编程有三大特性分别是,原子性,可见性,有序性。会产生这些特性的根本原因是现在的服务器都是多CPU多核心数的,每个CPU都有自己单独的一套缓存和pc系统,而且程序在运行时按照JMM的规范,它们是需要先把数据从主内存中读取到工作内存(也就是运行这个线程的CPU的缓存中),对工作内存中的数据进行修改之后再写回主内存,在对自己工作内存数据的操作对其他CPU是不可见的,这才会导致并发编程会有以上三种特性。下面就三种特性的概念,产生的问题,和解决方案进行阐述

image.png

1、原子性

1.1、原子性的定义

在并发编程中,会出现多个线程同时修改同一个变量的情况,此时对应的临界区域(多个线程都运行的代码区域)不做任何的限制的话,就会出现,a线程把i变量由1修改成2了,还没有来得及给i赋值2的时候,b线程也对i进行操作,但是它拿到的是原来的1,这样a,b两个线程都+1可结果还是2,为了解决这个问题,就需要保证a,b线程在对i修改之后把i写回主内存这一系列的操作,没有其他线程进行干扰,一次性全部完成之后,然后其他的线程再对i操作,由此就引出了并发编程中的原子性的定义

  • 原子性:保证多线程运行过程中,临界区域的代码在运行的时候,是不可分割不可被打断的,其间也不会有其他的线程对其进行干扰。

1.2、原子性的解决方案

1.2.1、通过synchronizd锁的方案
    private static int count;//通过synchronized实现public static synchronized void increment(){count++;}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 100000; i++) {increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000000; i++) {increment();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}
}

通过查看上面带synchronized底层编译的JVM操作指令可以发现,synchronized在JVM指令操作上在临界区域加上了monitorenter操作,直到临界区域完成才会有monitorexit(异常也会有一个monitorexit操作),退出操作

image.png

1.2.2、通过lock锁的方案
    private static int count;private static ReentrantLock lock = new ReentrantLock();public static void increment()  {//通过lock的方式实现lock.lock();try {count++;} finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 100000; i++) {increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100000; i++) {increment();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}

ReentrantLock的底层是基于AQS实现的,它是通过CAS的方式维护一个state变量来实现锁的操作

  • CAS(compare and swap)
    CAS是一条CPU层级就支持的原子操作,根据CAS的定义:比较和交换,底层的实现原理是,当它要置换内存中某个位置的值时,它会先去比较下是否和预期的值一致,如果一致它才置换。
  • CAS的缺点
    它只能保证对一个变量的操作是原子性的,不能实现对多行代码实现原子性。
  • CAS的问题
    • ABA问题:一个变量一开始是A,经过修改之后由A变成B,之后又变成A,此时对于那些引用类型是有问题的,因为引用类型虽然变回了A,但是它引用的指向的地址里的具体内容很可能已经发生了变化。
    • ABA问题的解决方案:在比较的时候不仅比较预期的值,还需要比较对应的版本号,这样在由A->B,又B->A的过程中,版本号肯定跟原先的不一致,由此可以判断出已经修改不能进行置换操作
1.2.3、通过ThreadLocal的方案
static ThreadLocal tl1 = new ThreadLocal();
static ThreadLocal tl2 = new ThreadLocal();public static void main(String[] args) {tl1.set("123");tl2.set("456");Thread t1 = new Thread(() -> {System.out.println("t1:" + tl1.get());System.out.println("t1:" + tl2.get());});t1.start();System.out.println("main:" + tl1.get());System.out.println("main:" + tl2.get());
}

ThreadLocal解决线程之间的原子方案是通过线程隔离实现的,直接把共享变量分配给每个线程,每个线程只能操作自己的这个变量,把共享变量变成线程私有的变量

  • ThreadLocal的底层实现
    1、每个线程都有一个ThreadLocalMap对象作为Thread的成员变量
    2、调用ThreadLocal的set方法时会初始化对应Thread的ThreadLocalMap变量
    3、把当前的ThreadLocal对象作为key,把对应要存的数据作为value存储
    image.png
  • ThreadLocal会产生内存泄露问题

由于一般情况在创建ThreadLocal的时候都会把它设置成Static,所以就算是a线程运行结束了,a线程的对应的ThreadLocal对象由于Static指向着所有不会释放内存,但是由于线程运行结束,线程产生的独有资源,ThreadLocalMap中的key和value应该释放掉,如果不释放新的线程不断产生,会不断的消耗系统内存,从而导致内存泄露

  • 解决方案

上述ThreadLocalMap的key和value内存泄露问题,其中key的内存泄露,系统已经帮我们做好了

  • 系统通过把key设置成WeakReference类型,能做到当这个线程运行完成,GC回收的时候就会把ThreadLocal对象回收。因为弱引用的特点是只要碰到GC回收,它就会被回收。
  • 至于value的回收,需要我们在代码里手动的调用ThreadLocal中的remove方法把value释放掉

2、可见性

2.1、可见性定义

导致可见性问题的原因是多CPU独立运行时,只会修改各自的工作内存也就是CPU缓存数据,而且多个CPU之间是有独立的缓存和PC系统。

  • 可见性:多线程在运行的时候一个线程修改了公共变量,其他的线程不能及时的见到

2.2、可见性的解决方案

2.2.1、volatile方案
    //通过volatile修饰,控制t1线程的停止private volatile static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {// ....}System.out.println("t1线程结束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主线程将flag改为false");}

上述代码中如果没有volatile修饰,flag在主线程里被修改成false,t1线程是不会感知到的,那volatile底层是怎么实现的呢?

  • volatile实现细节
    • volatile在JVM层面是通过带lock前缀的指令实现。
    • JVM的指令在CPU层面是通过缓存一致性协议,如MESI协议(inter的协议)实现。
    • volatile变量在写操作时,JVM会及时的把对应CPU的缓存行刷到主内存中,在volatile变量被修改之后,根据MESI协议,所有CPU中对应这行缓存行数据都会失效,得重新去主内存中读取。
2.2.2、synchronized或是lock方案

锁的方案是可以实现多线程间变量的可见性的。锁的定义就是让单个线程去修改共享变量,修改之后其他线程再去读取,这个时候其他线程读到的数据肯定是上个线程修改的最新数据

2.2.3、final方案

final修饰的变量初始化之后,就不能被修改,所有的线程拿到的都是同一个值,也是间接的实现了线程之间的可见性

3、有序性

3.1、有序性定义

由于CPU的运行速度和读取数据的速度相差好几个数量级的关系,所以现代CPU为了追求效率,会进行“乱序执行”,在运行到需要去内存中读数据的指令时,可能要花很长时间等待读取数据,在这些时间的等待中,在保证程序结果的最终一致性之后,CPU就进行指令重排,以提高效率,但是有些时候这中乱序是不被允许的。

  • 有序性:让程序中的指令集按照顺序执行,不让CPU进行指令重排

3.2、有序性解决方案

3.2.1、volatile方案
//通过volatile关键词解决
private static volatile MiTest test;
private MiTest(){}
public static MiTest getInstance(){// Bif(test  == null){synchronized (MiTest.class){if(test == null){// A   ,  开辟空间,test指向地址,初始化test = new MiTest();}}}return test;
}

上述是一个单例模式的样例代码,如果不加volatile关键词,在new MiTest这个共享对象的时候,就会出现问题,对象的new过程在底层一共有三个操作指令,分别是,给对象开辟空间,给对象初始化,把地址引用赋值给变量。如果此时发生了指令的重排,顺序变成了,给对象开辟空间,把地址引用赋值给变量,给对象初始化,那么B线程此时就会拿到没有进行初始化好的对象使用,就会发生问题,通过volatile防止指令重排就可以解决这个问题。

  • volatile防止指令重排的细节
    它是通过内存屏障来实现的,内存屏障相当于是一条指令,在这条指令的前后操作指令不能重排,在JVM层面有对应的读写屏障,在CPU底层也有对应的读写屏障来具体支持

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/65084.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【项目构建】Gradle入门

本文适用&#xff1a; 不知道什么是项目构建&#xff0c;可以了解下Ant&#xff0c;Maven&#xff0c;Gradle的区别。知道什么是项目构建&#xff0c;了解Ant&#xff0c;Maven&#xff0c;可以看到Gradle是怎么做的。知道什么是项目构建&#xff0c;了解Ant&#xff0c;Maven&…

java栈--数据结构

前言 java实现数据结构栈&#xff1a;用顺序表存储的栈和数组存储的栈。 本文源代码网址&#xff1a;https://gitee.com/zfranklin/java/tree/master/dataStructure/src/com/njupt/stack https://gitee.com/zfranklin/java/tree/master/dataStructure/src/com/njupt/stack 栈…

2.5.2 文件结构、目录及存取

文章目录 文件结构文件目录存取 文件结构 文件结构是文件的组织形式。从用户角度观察到的结构是逻辑结构&#xff0c;从机器实现存储的角度观察&#xff0c;看到的是物理结构。 逻辑结构 有结构的记录式文件&#xff1a;文件中记录的长度都相同&#xff0c;称为定长记录。文件…

关于科研中使用linux服务器的集锦

文章目录 常用的linux命令下载COCO2017数据集 常用的linux命令 一个文件移动到另一个目录下的命令是&#xff1a;mv -v ./old_name ./new_name 如果目标文件夹中已经有同名文件或文件夹&#xff0c;mv 会覆盖它们&#xff08;除非使用了 -i 选项来提示确认&#xff09;。 使用…

<项目代码>YOLO Visdrone航拍目标识别<目标检测>

项目代码下载链接 &#xff1c;项目代码&#xff1e;YOLO Visdrone航拍目标识别&#xff1c;目标检测&#xff1e;https://download.csdn.net/download/qq_53332949/90163918YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一…

java内存马

java内存马 idea 2024.1.2专业版 jdk1.8.0_181 tomcat 8.5.82 默认有java基础&#xff0c;Javassist&#xff0c;Jsp&#xff0c;JavaEE都会一点 更新ing 文章目录 java内存马0. 一些基础1. filter型内存马2. Servlet型内存马3. listener型内存马4. Tomcat特有的Valve内存马…

【ORB-SLAM3:相机针孔模型和相机K8模型】

在ORB-SLAM3中&#xff0c;相机的建模是 SLAM 系统的核心之一&#xff0c;因为它直接影响到如何处理和利用图像数据进行定位和地图构建。ORB-SLAM3 支持不同的相机模型&#xff0c;其中包括针孔模型和鱼眼模型&#xff08;K8 模型&#xff09;。下面分别介绍这两种模型。 相机…

[手机Linux] 七,NextCloud优化设置

安装完成后在个人设置里发现很多警告&#xff0c;一一消除。 只能一条一条解决了。 关于您的设置有一些错误。 1&#xff0c;PHP 内存限制低于建议值 512 MB。 设置php配置文件&#xff1a; /usr/local/php/etc/php.ini 把里面的&#xff1a; memory_limit 128M 根据你自…

P3456 [POI2007] GRZ-Ridges and Valleys BFS-连通块思想

题目描述 Byteasar loves trekking in the hills. During the hikes he explores all the ridges and valleys in vicinity. Therefore, in order to plan the journey and know how long it will last, he must know the number of ridgesand valleys in the area he is goi…

一键打断线(根据相交点打断)——CAD c# 二次开发

多条相交线根据交点一键打断&#xff0c;如下图&#xff1a; 部分代码如下: finally namespace IFoxDemo; public class Class1 {[CommandMethod("ddx")]public static void Demo(){//"ifox可以了".Print();Database db HostApplicationServices.Workin…

【Yonghong 企业日常问题 06】上传的文件不在白名单,修改allow.jar.digest属性添加允许上传的文件SH256值?

文章目录 前言问题描述问题分析问题解决1.允许所有用户上传驱动文件2.如果是想只上传白名单的驱动 前言 该方法适合永洪BI系列产品&#xff0c;包括不限于vividime desktop&#xff0c;vividime z-suit&#xff0c;vividime x-suit产品。 问题描述 当我们连接数据源的时候&a…

在Windows11上编译C#的实现Mono的步骤

在Windows11上编译Mono的步骤 1、 在win11打开开发者模式,在更新和安全选项里,如下图: 2、下载并安装64位的cygwin, 下载网站:www.cygwin.com 3、 安装 Visual Studio 2015 or later 的社区版本。 4、 下载Mono的windows最新版本。 5、 在cmd.exe里运行下面的命令来安…

嵌入式轻量级开源操作系统:HeliOS的使用

嵌入式轻量级开源操作系统:HeliOS的使用 &#x1f4cd;项目地址&#xff1a;https://github.com/heliosproj/HeliOS HeliOS项目是一个社区交付的开源项目&#xff0c;用于构建和维护HeliOS嵌入式操作系统&#xff08;OS&#xff09;。HeliOS是一个功能齐全的操作系统&#xff0…

黑马Java面试教程_P9_JVM虚拟机

系列博客目录 文章目录 系列博客目录前言1. JVM组成1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f;3 41.2 什么是程序计数器&#xff1f;3 4总结 1.3 你能给我详细的介绍Java堆吗? 3 4总结 1.4 什么是虚拟机栈 3 4总结 1.6 能不能解释一下方法区&#xff1f; 3…

YOLO11改进-注意力-引入多尺度卷积注意力模块MSCAM

如何在增强特征图的同时降低计算成本&#xff0c;以提升模型性能。基于此&#xff0c;MSCAM 模块采用了多尺度卷积注意力机制&#xff0c;通过 CAB、SAB 和 MSCB 三个子模块协同工作。CAB 利用自适应池化和卷积操作生成通道注意力权重&#xff0c;强调重要通道特征&#xff1b;…

Kafka无锁设计

前言 在分布式消息队列系统中,Kafka 的无锁设计是其高吞吐量和高并发的核心优势之一。通过避免锁的竞争,Kafka 能够在高并发和大规模的生产环境中保持高效的性能。为了更好地理解 Kafka 的无锁设计,我们首先对比传统的队列模型,然后探讨 Kafka 如何通过无锁机制优化生产者…

Refusal in Language Models Is Mediated by a Single Direction

开源代码&#xff1a;https://github.com/andyrdt/refusal_direction Abstract 会话型大语言模型针对指令遵循和安全性进行了微调&#xff0c;从而产生服从良性请求但拒绝有害请求的模型。虽然这种拒绝行为在聊天模型中普遍存在&#xff0c;但其背后的机制仍然知之甚少。在这…

智慧平台数据可视化解决方案(附实践资料下载)

阿里云详解数据可视化大屏构建&#xff0c;内容涉及数据可视化大屏的构建。以下是该文件的核心内容概要&#xff1a; 数据可视化大屏概述&#xff1a; 数据可视化大屏的定义和目的。大屏在企业决策中的作用。 大屏设计原则&#xff1a; 强调用户体验和视觉美感。信息的清晰度和…

基于openlayers 开发vue地图组件

先看效果 主要功能如下&#xff1a; 测量图源更换放大缩小地图添加点hover点数据切换到地图位置&#xff1b;也设定层级2D3D切换&#xff0c;3D为cesium开发&#xff0c;技术交流可以加V&#xff1a;bloxed 地图工具做了插槽&#xff0c;分为toolbar&#xff08;左上角工具…

人工智能ACA(六)--计算机视觉基础

一、计算机视觉概述 1. 计算机视觉定义 人工智能&#xff08;AI&#xff09;的一个重要分支旨在使计算机和系统能够从图像或多维数据中“理解”和“解释”视觉世界通过模拟人类视觉系统&#xff0c;计算机视觉技术能够自动执行诸如识别、分类、检测和跟踪等任务。 2. 计算机…