Java并发—Java内存模型以及线程安全

news2024/9/20 22:22:45

目录

 

一、Java内存模型

JMM的核心概念

二、什么是线程安全? 

1、原子性

2、有序性

3、可见性

三、如何确保线程安全?

1、sychronized关键字

2、Lock接口和其实现

3、volatile关键字

4、Atomic原子类

5、ThreadLocal

6、不可变对象

7、并发集合类

8、并发工具类

9、Future和Callable


一、Java内存模型

在谈及线程安全前,需要了解内存模型一下

Java内存模型(Java Memory Model,JMM)是一种抽象的概念,JMM并不真实存在,它是一种规范规定了在Java并发编程中如何处理多线程之间的内存交互程序中变量在内存中的访问方式

5eb5081162df4468bfecc70ea194214c.jpeg

JMM的核心概念

1、主内存与工作内存:

主内存:是所有线程共享的内存区域,所有变量都存储在主内存中,主内存是共享的

工作内存每个线程都有自己的工作内存(局部变量存储区),线程对共享变量的所有操作都发生在工作内存中,然后同步回主内存

这种模型允许线程在本地缓存共享变量的副本,提高性能,但也带来了同步的复杂性

2、内存屏障

内存屏障(Memory Barrier)是JMM中用于控制内存访问顺序的指令。它确保指令序列中的内存读写操作按照特定的顺序执行,从而保证线程间的内存可见性和有序性

3、Happens-Before规则

Happens-Before是JMM中最核心的概念之一,它定义了一组偏序关系,用于判断两个操作之间的内存可见性和有序性,用于描述多线程程序中操作的执行顺序,确保了线程之间的正确通信和数据一致性

简而言之:如果在同一个线程中,操作A在操作B之前执行,那么我们说一个操作A happens-before 另一个操作B,那么A的执行结果对B是可见的,且A的执行顺序排在B之前,也就是先执行的操作的结果必须对后执行的操作可见

用一个例子来深入了解内存模型

🌰:线程A和线程B从主内存读取和修改x=1的过程

419f7d591bdf461fa9f107e3838ff3e4.png

线程A和线程B要进行通信的话,必须要进行以下2个步骤:

  1. 初始化:x = 1,存储在主内存。

  2. 线程A读取:A从主内存读取x,复制值1到A的工作内存。

  3. 线程B读取:B从主内存读取x,复制值1到B的工作内存。

  4. 线程A修改:A在工作内存中修改x至2

  5. 线程A写回:A将工作内存中的x值2写回主内存。

  6. 线程B重新读取:B从主内存读取最新的x值2,保证了数据的可见性。

这个过程展示了JMM如何确保多线程环境下的数据一致性

二、什么是线程安全? 

线程安全:在多线程环境下,多个线程可以安全地访问和操作共享数据,而不会引发数据不一致或程序错误的问题

JMM 解决并发程序中最关键的两个问题:线程间的可见性指令重排序

  • 线程间的可见性:确保当一个线程修改了共享变量的值时,其他线程可以立即看到这一改变。没有良好的可见性保证,一个线程对共享变量的修改可能对其他线程不可见,导致数据不一致。
  • 指令重排序:为了提高性能,编译器处理器常常会改变指令的执行顺序(只要这种改变不影响单线程内的程序逻辑)。然而,在多线程环境下,这种重排序可能导致严重问题

实际上,线程安全问题的具体表现体现在三个方面,原子性有序性可见性,JMM 通过以下几种方式来控制和协调上述现象:

1、原子性

原子性:一个操作要么全部完成,要么全部不完成,不会被中断

JMM保证了基本类型的读取和写入操作是原子的,即不可分割的。但是,复合操作(如i++)需要额外的同步机制(Automic类)才能保证原子性

例如:两个线程同时尝试修改同一个变量,原子性可以确保这个修改操作作为一个整体被执行,而不是被另一个线程业务

场景:银行取钱,你想要从账户中取出100元。在理想情况下,这个操作应该是“原子”的,也就是说,要么整个取款操作成功完成(你拿到了100元,账户余额减少了100元),要么操作完全不发生(你没拿到钱,账户余额不变)。中间不能出现任何状态,比如你只拿到了50元而账户扣除了100元,这会导致数据不一致

2、有序性

有序性保证了指令的执行顺序不会导致数据依赖关系的破坏

JMM限制了编译器和处理器对指令的重排序,以避免破坏程序的语义。volatilesynchronizedfinal等关键字提供了内存屏障,强制执行特定的内存操作顺序,从而保证了线程之间的正确交互

例如:如果一个线程先读取变量A,然后读取变量B,有序性确保在另一个线程中,如果先修改了B,然后再修改A,那么第一个线程读取B时,不会看到A的更新值,因为它应该先读取A的旧

场景:排队买票,如果你前面的人买完票后,后面的人突然插队到你前面买了票,这就打破了正常的顺序

3、可见性

可见性当一个线程修改了一个共享变量后,其他线程是否能够看到这个修改

JMM规定,当一个线程修改了共享变量的值,新值对其他线程来说是可见的。通常通过volatile关键字、synchronized代码块或方法、以及final字段来实现

场景:五子棋游戏,你在游戏中放置了黑棋,然后告诉朋友“我已经在某个位置下了黑棋”。为了游戏能够正常进行,你的朋友必须能看到你刚下的黑棋的位置,才能做出相应的策略

这三个概念对于多线程编程非常关键,它们确保了程序在并发执行时的正确性和一致性。通过使用、volatile关键字、原子变量等机制,程序员可以控制和管理多线程环境下的原子性、可见性和有序性,从而避免数据竞争、死锁等问题,使程序能够正确地运行

三、如何确保线程安全?

这里大概叙述一下,后续文章进行详细补充,具体可以采取以下几种策略:

1、sychronized关键字

        确保同一时间只有一个线程可以访问资源。

public synchronized void method() {
    // 访问和修改共享资源
}

2、Lock接口和其实现

使用java.util.concurrent.locks包中的ReentrantLock等类允许更细粒度的锁控制,如公平锁、非公平锁、读写锁等

private final ReentrantLock lock = new ReentrantLock();

public void method() {
    lock.lock();
    try {
        // 访问和修改共享资源
    } finally {
        lock.unlock();
    }
}

3、volatile关键字

用于变量,确保变量的读写操作具有可见性,即一个线程对变量的修改对其他线程立即可见。volatile不保证原子性,但对于简单的读写操作(如对基本类型的读写)

4、Atomic原子类

使用java.util.concurrent.atomic包中的原子变量类,如AtomicIntegerAtomicReference等,这些类通过底层的硬件支持实现高效的原子操作。

private AtomicInteger count = new AtomicInteger(0);

public void increment() {
    count.incrementAndGet();
}

5、ThreadLocal

ThreadLocal变量为每个线程提供独立的副本,避免了线程间的共享和同步问题

6、不可变对象

设计不可变对象(immutable objects),即对象在创建后其状态不可更改,这样可以避免多线程访问时的同步问题。例如:String、Integer等包装类都是不可变的

public final class ImmutableClass {
    private final int value;

    public ImmutableClass(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

7、并发集合类

使用java.util.concurrent包中的线程安全集合类,如ConcurrentHashMapCopyOnWriteArrayList等,这些类已经内建了线程安全机制。

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key", 1);

8、并发工具类

CountDownLatchCyclicBarrierSemaphore等,提供了更复杂的线程同步手段

9、FutureCallable

可以用于异步执行任务,内部使用线程池和同步机制来保证线程安全

通过这些机制,Java可以在多线程环境中有效地管理资源访问,保持数据的一致性和正确性

其中

  • Volatile 实现可见性、有序性

当一个线程修改了一个volatile变量的值,这个修改会立即被其他线程可见,无需等待主内存和工作内存之间的数据同步

  • CAS 实现原子性

Java中的CAS(Compare and Swap,比较并交换)是一种无锁技术,当多个线程同时尝试对同一个共享变量执行CAS操作时,只有一个线程会比较成功并更新变量的值,其他线程则会因为比较失败而需要重新尝试,相对同步机制,性能会有一定的优化

  • Lock 实现原子性、可见性、有序性

Java中接口定义了锁的操作接口,它的实现类,如ReentrantLock,内部持有一个AQS对象,AQS通过内部维护一个同步状态,以及一个FIFO队列来管理获取锁的线程。而这个管理线程的过程就会涉及到CAS和volatile的的操作

  • Synchronized 实现原子性、可见性、有序性

同一时间只有一个线程可以执行synchronized的代码,意味着这个线程在执行时不会被干扰,其他线程也会按照预定的顺序执行同步代码,这样就实现了原子性和有序性。

而在该线程执行期间,它会直接在主内存中读取操作共享变量,当下个线程执行代码块时,也就会看到最新的数据,从而实现了可见性

 

 

 

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

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

相关文章

电商数据采集封装API的详细步骤分享(API测试实例)

在当今的电商行业中&#xff0c;数据采集已成为企业获取市场洞察、优化运营策略、提升用户体验的重要手段。而封装电商数据采集的API接口&#xff0c;则是将这一复杂过程标准化、模块化的有效方式。本文将详细分享电商数据采集封装API的步骤&#xff0c;并通过一个实际的API测试…

努力努力努力的第十四天(2024.7.31)

昨天日期写错了写成2020.7.30,应该是2024.7.31&#xff08;手滑了哈哈哈&#xff09; 1.行列转换 效果演示&#xff1a; 这是未经行列转换操作的t_score表&#xff1a; 这是经过行列转换后的t_score表&#xff1a; 第一步&#xff1a;确定初步的做法 使用分组查询(group by…

20240731在WIN10下数框框的方法【CPU】

20240731在WIN10下数框框的方法【CPU】 2024/7/31 20:14 百度&#xff1a;WINDOWs 10 多核CPU 数框框 win10怎么数框框 1、首先打开win10系统&#xff0c;进入任务管理器。 2、其次点击CPU使用率窗口&#xff0c;鼠标悬停在右边的窗口按右键。 3、最后将图形更改为&#xff1a;…

【计算机网络】http协议的原理与应用,https是如何保证安全传输的

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

优化|高性能模块预测控制求解器

本文介绍的是另外一种针对模块控制MPC问题的一种高效求解方法。High-Performance Small-Scale Solvers for Linear Model Predictive Control&#xff0c;Gianluca Frison, Hans Henrik Brandenborg Srensen, Bernd Dammann, John Bagterp Jrgensen [1]。本文讲了HPIPM的前身&a…

Phalco安装过程以及踩的一些坑(mac环境)

一 背景 公司用Phalcon框架好长时间了,中途发现了一些Phalcon使用的上的问题,于是想在本地搭建一套Phalcon的环境,方便排查问题使用。 二 Mac系统下的安装 看了很多说法,最终发现还是官网给力,安装Phalcon使用下列命令即可(前提条件是PHP已安装好,工具pecl也安装好了):…

2024-07-31 Android studio gradle、sdk、ndk 等路径记录

一、gradle的下载路径&#xff1a;C:\Users\user\.gradle\wrapper\dists 二、NDK下载路径C:\Users\user\AppData\Local\Android\Sdk\ndk 三、SDK下载路径&#xff1a;C:\Users\user\AppData\Local\Android\Sdk\platforms

数据结构的基本概念与算法

数据结构的基本概念与算法 什么是数据&#xff1f; 数据是信息的载体&#xff0c;是描述客观事物属性的数、字符以及所有能输入到计算机中并被计算机程序识别和处理的符号的集合&#xff1b;总结来说 -> 数据就是计算机程序加工的原料&#xff1b; 数据元素、数据项&#xf…

Python+Vue3+onlyoffice开发私有化文档管理系统实战笔记 20240731

之前进度 表字段设计如下&#xff1a; const columns [{title: 名称,key: name,dataIndex: name,},{title: 类型,key: category,dataIndex: category,},{title: 作者,key: author,dataIndex: author,},{title: 路径,key: path,dataIndex: path,},{title: 最近访问时间,key: l…

【Spark计算引擎----上篇:(全网最详细)带你从零基础通往精通之路】

前言&#xff1a; &#x1f49e;&#x1f49e;大家好&#xff0c;我是书生♡&#xff0c;本阶段和大家一起分享和探索大数据技术–spark计算引擎&#xff0c;本篇文章主要讲述了&#xff1a;spark的概念&#xff0c;spark的起源&#xff0c;Spark的计算框架&#xff0c;Spark组…

Python爬虫入门03:用Urllib假装我们是浏览器

文章目录 引言Urllib库简介Request模块详解Error模块与异常处理Parse模块与URL解析Robotparser模块 模拟浏览器请求使用Request方法添加请求头信息代码示例1. 设置请求URL和请求头2. 定义请求参数并转换为适当的格式3. 使用Request方法封装请求4. 发送请求并获取响应 常用使用方…

项目实战_表白墙(升级版)

你能学到什么 表白墙&#xff08;升级版&#xff09;Mybatis的一些简单应用 正文 前⾯的案例中, 我们写了表⽩墙, 但是⼀旦服务器重启, 数据就会丢失. 要想数据不丢失, 需要把数据存储在数据库中&#xff0c;接下来咱们借助MyBatis来实现数据库的操作。 数据准备 如果我们…

Linux-用户和用户组管理

用户管理 创建 useradd USERNAME&#xff08;名字&#xff09; [rootlocalhost ~]# useradd bao 创建指令 怎么判断创建成功与否&#xff08;是否报错&#xff09; 观察是否报错可行但用变量的方式判断会更加直观 [rootlocalhost ~]# echo $? 变量 什么是变量&…

Vant2框架tab切换list不加载问题(切换后onload不继续触发)

遇到的问题&#xff1a; van-list 加载&#xff0c;页面上有个切换的 van-tab&#xff0c;实现切换不同的类型&#xff0c;加载不同的list的功能。 Vant2 的 List 列表 就有个 bug&#xff0c;当切换 tab 的时候&#xff0c;它的 onload 没有自动触发。 然而在当前标签页&#…

AMD第二季度财报:数据中心产品销售激增,接近总收入一半

#### 财报亮点 7月30日&#xff0c;AMD公布了截至6月29日的第二季度财务业绩&#xff0c;利润超过了华尔街的预期。根据TechNews的报道&#xff0c;最值得注意的是&#xff0c;AMD现在近一半的销售额来自于数据中心产品&#xff0c;而非传统的PC芯片、游戏主机或是工业与汽车嵌…

IDEA打开项目显示在顶端

问题 IDEA打开项目显示在顶端&#xff0c;更改到左侧 解决方案 删除项目中.idea文件,重新打开

【pikachu靶场】跨站脚本攻击详细教程Cross-Site Scripting(xss)

文章目录 XSS&#xff08;跨站脚本&#xff09;概述 1.反射型xss&#xff08;get&#xff09; 2.反射型xss&#xff08;post&#xff09; 3.存储型XSS 4.DOM型xss 5.DOM型XSS-X 6.xss之盲打 7.xss之过滤 8.xss之htmlspecialchars 9.xss之href输出 10.xss之js输出 XSS…

并发编程相关面试题-Synchronized

重量级锁实现的原理 1、synchronized是JVM内置锁&#xff0c;基于Monitor机制实现&#xff0c;依赖底层操作系统的互斥锁Mutex&#xff08;互斥量&#xff09;&#xff0c;它是一个重量级锁、 2、synchronized同步方法是通过方法中的access_flags中设置ACC_SYNCHRONIZED标志来…

换根dp学习总结3

我也不想搞这么多&#xff0c;但是这东西真的太难了&#xff0c;因为我还是个蒟蒻。算了蒟蒻继续写这次的总结了 寻找全图最远路径问题——Computer ——题目来源于hdu2196 题意&#xff1a;题目就是说会输入多组数据&#xff0c;每组数据给你一个n&#xff0c;表示结点的总数…

怎么评价程序员40岁了竟然还在撸代码?

怎么评价外卖员40岁了竟然还在送外卖&#xff1f; 怎么评价滴滴司机40岁了竟然还在跑滴滴&#xff1f; 怎么评价老师40岁了竟然还在教书&#xff1f;难道程序员的本职工作不是敲代码吗&#xff1f;无论你是管理层还是螺丝钉&#xff0c;工业环境下怎么可能一行代码都不敲呢&…