java 线程安全的原因_java的多线程:java安全问题产生的原因与JMM的关系

一、多线程产生安全问题

1、Java内存模型
共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。

从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:

线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。

本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

1.主内存:存放多线程操作的共享变量 比如count=0

2.本地内存:存储了当前该线程以读/写共享变量的副本,副本 count1=0,count2=0

73cdc79cc50996ba44b1db93b24288b9.png

多线程同时进行变量操作时比如,对count进行++,先对副本进行++为1,将副本刷新到主内存,由于两个线程是不可见的,共享变量变为1,理论为2,但是还是1 ,

2.不可见

b837ad980a2338bc99d6b2e7dbd698cc.png

3.可见性

线程A与线程B之间如要通信的话,必须要经历下面2个步骤:

  1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。

  2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。

下面通过示意图来说明这两个步骤:

802c8d5feb3652c47127bff37b0775ed.png

二、Volatile

什么是Volatile

可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,可以立即获取修改之后的值。

在Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或是CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰符的变量则是直接读写主存。

Volatile 保证了线程间共享变量的及时可见性,但不能保证原子性

class ThreadVolatileDemo extends Thread {public  boolean flag = true;@Overridepublic void run() {undefinedSystem.out.println("开始执行子线程....");while (flag) {undefined}System.out.println("线程停止");}public void setRuning(boolean flag) {this.flag = flag;}}public class ThreadVolatile {public static void main(String[] args) throws InterruptedException {ThreadVolatileDemo threadVolatileDemo = new ThreadVolatileDemo();threadVolatileDemo.start();Thread.sleep(3000);threadVolatileDemo.setRuning(false);System.out.println("flag 已经设置成false");Thread.sleep(1000);System.out.println(threadVolatileDemo.flag);}}

运行结果:

已经将结果设置为fasle为什么?还一直在运行呢。

原因:线程之间是不可见的,读取的是副本,没有及时读取到主内存结果。

解决办法使用Volatile关键字将解决线程之间可见性, 强制线程每次读取该值的时候都去“主内存”中取值

2、Volatile特性

1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。

2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。

volatile 性能:

volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

(1)从而我们可以看出volatile虽然具有可见性但是并不能保证原子性。

(2)性能方面,synchronized关键字是防止多个线程同时执行一段代码,就会影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized。

但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。

ad724ed90aa7a85a575134494c686688.png

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

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

相关文章

python用pip安装wordcloud_如何在python3.7中使用pip安装wordcloud

我一直试图在windows10上pip安装python3.7的wordcloud,但我一直收到这个错误消息,我不太理解,也无法修复。在正在为wordcloud运行setup.py安装:已完成,状态为“error”Complete output from command "c:\program …

分布式系统全局唯一ID的几种实现方式

现如今可谓是微服务、分布式、IoT(物联网)横行的时代,作为一名开发者始终还是要保持一定的危机意识,特别是在日常的项目开发中,若是有机会接触到一些关于微服务、分布式下的应用场景,应当硬着头皮、排除万难…

git如何查看缓存区文件内容_详解Git工作区、暂存区、历史记录区以及git reset、git revert、git checkout等撤销命令的区别...

一、可以将git简单的分为三个区域 1、工作区(working directory) 2、暂缓区(stage index) 3、历史记录区(history) 如图:其中git add files 把当前工作目录中的文件放入暂存区域这其实做了两件事: 1、将本地文件的时间戳、长度&#xff0…

分布式全局唯一ID的实现

分布式全局唯一ID的实现 前言 上周末考完试,这周正好把工作整理整理,然后也把之前的一些素材,整理一番,也当自己再学习一番。 一方面正好最近看到几篇这方面的文章,另一方面也是正好工作上有所涉及,所以决…

mysql多个分类取n条_MySQL获取所有分类和每个分类的前N条记录

MySQL获取所有分类和每个分类的前N条记录。比如有文章表 test(Id,type,tiem),现在要用SQL找出每种类型中时间最新的前N个数据组成的集合,一段不错的代码,留存备用。SELECT a1.* FROM test a1INNER JOIN (SELECT a.type,p.time FROM test aLEF…

Java接口学习(接口的使用、简单工厂、代理模式、接口和抽象类的区别)

前言引入 官方解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能&#xff0…

java 矩阵转置_图解利用Java实现数组转置

我们编写Java代码,如下图所示:package com.tina;public class demo {public static void main(String args[]) {int data[] new int[] { 1, 6, 3, 9, 5, 7, 2, 0, 4, 8 };PrintArray(data);}// 输出数组内容public static void PrintArray(int arr[]) {…

java中static、final、static final浅析

final final可以修饰类、属性、方法、局部变量、参数,不能修饰接口!final修饰类:该类不能被继承(解释了为什么不能修饰接口,不过接口里面的属性、方法等是可以用final修饰的);final修饰属性&am…

最短路径 floyd java_java实现Floyd算法求最短路径

关于无向图的最短路径问题:这个程序输出:最短路径矩阵例如:W[0][5]9 代表vo->v5的最短路径为9W:0 1 3 7 4 91 0 2 6 3 83 2 0 4 1 67 6 4 0 3 24 3 1 3 0 59 8 6 2 5 0package com.xh.Floyd;import java.util.ArrayList;public class Floyd_01 {publi…

SpringBoot 使用 log4j2

一、新建工程 选择一些基础依赖 填写工程名称和项目路径 二、工程配置 修改文件编码格式 设置Java Compiler 修改maven配置文件路径 三、pom.xml的web依赖中排除掉logging依赖&#xff0c;并且引入log4j2依赖 <dependency><groupId>org.springframework.…

springBoot 通过使用log4j2

1.排除 Spring-boot-starter 默认的日志配置 将原本的 spring-boot-starter 改为 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><exclusions><exclusion><groupId>…

java finalize 何时被调用_finalize()方法什么时候被调用?析构函数(final

finalize()方法也叫收尾方法。一旦垃圾回收器准备好释放对象占用的存储空间&#xff0c;首先会去调用finalize()方法①进行一些必要的清理工作(对垃圾回收器不能处理的特殊情况进行处理)(例子在下边)②也有可能使该对象重新被引用&#xff0c;我习惯叫这种作用为复活。注意&…

编程和java是什么关系_C语言和Java编程有什么区别?

C语言和Java编程有什么区别&#xff1f;Java从根本上说是c之后的一种改进语言&#xff0c;纯面向对象的一种编程语言(当然比起Ruby还是差一点)&#xff0c;有了C语言的基础固然对学习Java有帮助&#xff0c;因为在某种程度上Java和C语言是比较接近的。但是如果没有学习过C语言也…

SpringBoot默认日志logback配置解析

SpringBoot默认日志logback配置解析 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候&#xff0c;是带着下面几个问题来查资料的&#xff0c;你呢 如何引入日志&#xff1f;日志输出格式以及输出方式如何配置&#xff1f;代码中如何使用&#xff1f; 正文…

java lang报错_java.lang.UnsupportedClassVersionError:JDK版本不一致报错

08-15 14:13:29 ERROR doPost(jcm.framework.rmi.RMIServlet:155) -SchedulerService.forceRunJobFlow error.未指定错误&#xff0c;请查看详细信息at jcm.framework.rmi.ClientService.execute(ClientService.java:129)at ...(...)at jcm.flowengine.impl.JobFlowEngine.runJ…

SpringBoot 之Spring Boot Starter依赖包及作用

spring-boot-starter 这是Spring Boot的核心启动器&#xff0c;包含了自动配置、日志和YAML。 spring-boot-starter-amqp 通过spring-rabbit来支持AMQP协议&#xff08;Advanced Message Queuing Protocol. 。 spring-boot-starter-aop 支持面向方面的编程即AOP&#xff0…

java中怎么判断相等_Java中判断相等 (== 与 .equals())

1.Java中有两种判断相等的方法&#xff1a;1.1首先是运算符对于基本类型而言&#xff0c;运算符比较的是值是否相等(本质也是比较的地址&#xff0c;因为常量在常量池中的地址不可改变)int a 3;int b 3;System.out.println(ab);//结果为true对于引用类型而言&#xff0c;运算…

SpringBoot查看和修改依赖的版本

springBoot依赖管理&#xff1a; 1、引入父项目的作用是实现对所有依赖的管理。 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version> &l…

java wait 释放锁_JAVA锁之wait,notify(wait会释放锁,notify仅仅只是通知,不释放锁)...

wait是指在一个已经进入了同步锁的线程内&#xff0c;让自己暂时让出同步锁&#xff0c;以便其他正在等待此锁的线程可以得到同步锁并运行&#xff0c;只有其他线程调用了notify方法(notify并不释放锁&#xff0c;只是告诉调用过wait方法的线程可以去参与获得锁的竞争了&#x…

在IDEA中解决jar包冲突的神操作-必看

在开发过程中&#xff0c;经常会遇到导入jar包后jar包冲突的情况&#xff0c;大家也都知道&#xff0c;解决jar包冲突通常都比较麻烦&#xff0c;要找到多余的依赖&#xff0c;把低版本的依赖去掉。而大家通常能搜到IDEA解决jar包冲突的方法&#xff0c;应该是这样的&#xff1…