java volatile 关键字详解「建议收藏」

java volatile 关键字详解「建议收藏」javavolatile关键字详解一,什么是volatile关键字,作用是什么​volatile是java虚拟机提供的轻量级同步机制​作用是:1.保证可见性2.禁止指令重排3.不保证原子性本篇具体就讲解什么叫保证了可见性,什么叫禁止指令重排,什么是原子性而在这之前需要对JMM有所了解二,什么是JMM​JMM(java内存模型JavaMemoryModel简称JMM)本身是一个抽象的概念,并不在内存中真实存在的,它描述的

大家好,又见面了,我是你们的朋友全栈君。

java volatile 关键字详解

一,什么是volatile关键字,作用是什么

​ volatile是java虚拟机提供的轻量级同步机制

​ 作用是: 1.保证可见性 2.禁止指令重排 3.不保证原子性

本篇具体就讲解 什么叫保证了可见性, 什么叫禁止指令重排,什么是原子性

而在这之前需要对JMM 有所了解

二,什么是JMM

​ JMM(java 内存模型 Java Memory Model 简称JMM) 本身是一个抽象的概念,并不在内存中真实存在的,它描述的是一组规范或者规则,通过这组规范定义了程序中各个变量(实例字段,静态字段和构成数组对象的元素)的访问方式.

JMM的同步规定:

​ 1.线程解锁之前,必须把共享变量刷新回主存

​ 2.线程加锁锁之前,必须读取主存的最新值到自己的工作空间

​ 3.加锁解锁必须是 同一把锁

​ 由于 JMM运行程序的实体是线程.而每个线程创建时JMM都会为其创建一个自己的工作内存(栈空间),工作内存是每个线程的私有 数据区域.而java内存模型中规定所有的变量都存储在主内存中,主内存是共享内存区域,所有线程都可以访问,但线程的变量的操作(读取赋值等)必须在自己的工作内存中去进行,首先要 将变量从主存拷贝到自己的工作内存中,然后对变量进行操作,操作完成后再将变量操作完后的新值写回主内存,不能直接操作主内存的变量,各个线程的工作内存中存储着主内存的变量拷贝的副本,因IC不同的线程间无法访问对方的工作内存,线程间的通信必须在主内存来完成, 其简要访问过程如下图:

在这里插入图片描述

三,可见性

​ 可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

​ 通过前面的 JMM介绍,我们知道各个线程对主内存的变量的操作都是各个线程各自拷贝到自己的工作内存中进行操作,然后在写回主内存中

​ 这就可能存在一个线程a修改了共享变量X的值但还未写回主内存,又有一个线程b对共享变量X进行操作,但 此时线程a的工作内存的共享变量X对线程吧来说是不可见的,这种工作内存与主内存同步延迟的问题就造成了可见性问题

四,不保证原子性

​ 原子性:某个线程在执行某项业务时,中间不可被加塞或分割,需要整体完整。要么同时成功,要么同时失败

    class MyData{ 
volatile int number = 0;
​    Object object = new Object();
public void addTo60(){ 

this.number = 60;
}
public void addPlusPlus(){ 

this.number++;
}
AtomicInteger atomicInteger = new AtomicInteger();
public void addAtomic(){ 

atomicInteger.getAndIncrement();
}
}
/** * 验证volatile的可见性 * 1.当number未被volatile修饰时,new Thread将number值改为60,但main线程并不知道,会一直在循环中出不来 * 2.当number使用volatile修饰,new Thread改变number值后,会通知main线程主内存的值已被修改,结束任务。体现了可见性 * * 验证volatile不保证原子性 * 1.原子性是指,某个线程在执行某项业务时,中间不可被加塞或分割,需要整体完整。要么同时成功,要么同时失败 * * 如何解决呢? * 1.使用synchronize * 2.使用AtomicInteger * */
public class VolatileDemo { 

public static void main(String[] args) { 

//seeByVolatile();
atomic();
}
//验证原子性
public static void atomic() { 

MyData myData = new MyData();
for (int i = 1; i <= 20; i++) { 

new Thread(new Runnable() { 

@Override
public void run() { 

for (int j = 1; j <= 1000; j++) { 

/*synchronized (myData.object){ myData.addPlusPlus(); }*/
myData.addPlusPlus();
myData.addAtomic();
}
}
}).start();
}
//等待上面20个线程全部计算结束
while (Thread.activeCount() > 2){ 

Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "int finally number is " + myData.number);
System.out.println(Thread.currentThread().getName() + "AtomicInteger finally number is " + myData.atomicInteger);
}
//验证可见性的方法
public static void seeByVolatile() { 

MyData myData = new MyData();
//第一个线程
new Thread(){ 

public void run(){ 

System.out.println(Thread.currentThread().getName() + " come in");
try { 

sleep(3000);
} catch (InterruptedException e) { 

e.printStackTrace();
}
myData.addTo60();
System.out.println(Thread.currentThread().getName() + " update number to " + myData.number);
}
}.start();
//第二个线程 main
while (myData.number == 0){ 

}
System.out.println(Thread.currentThread().getName() + "mission is over");
}
}

number++在多线程下是非线程安全,不是原子性操作?

在这里插入图片描述

五,禁止指令重排

​ 计算机在执行程序时,为了提高性能,编译器和处理 器常常会对指令做重排,一般分为一下三种:

在这里插入图片描述

单线程的环境里指令重排确保最终执行的结果和代码顺序执行的结果一致

处理器在进行指令重排是必须 要考虑指令之间的数据依赖性

多线程的环境交替执行,由于编译器优化重排的存在,俩个线程使用变量能否保证一致性是无法确定的,无法预料的

实例一:

在这里插入图片描述

实例二:

在这里插入图片描述

线程操作资源类,线程1访问method1,线程2访问method2,正常情况顺序执行,a=6
多线程下假设出现了指令重排,语句2在语句1之前,当执行完flag=true后,另一个线程马上执行method2,a=5

所以volatile 禁止指令重排,从而避免多线程的 环境下出现执行乱序 的情况

六:使用volatile 的经典案例

 单例DCL的代码
public class SingletonDemo { 

private static SingletonDemo instance = null;
private SingletonDemo(){ 

System.out.println(Thread.currentThread().getName() + "构造方法");
}
//DCL双端加锁机制
public static SingletonDemo getInstance(){ 

if (instance == null){ 

synchronized (SingletonDemo.class){ 

if (instance == null){ 

instance = new SingletonDemo();
}
}
}
return instance;
}
}

这种写法在多线程条件下可能正确率为99.999999%,但可能由于指令重排出错

原因在于某一个线程执行到第一次检测,读取到instance不为null,instance引用对象可能还没有完成初始化.

instance = new SingletonDemo();; 分为一下三步

  1. memory = allocate() //分配内存
  2. ctorInstanc(memory) //初始化对象
  3. instance = memory //设置instance指向刚分配的地址

2 ,3 步不存在数据依赖, 可以指令重排的执行顺序为 1 ,3 ,2,设置instance指向刚分配的地址,次数instance还没有初始化完

但此时instance不为null了,若正好此时有一个线程来访问,就出现了线程安全问题

所以需要添加volatile 关键字

public class SingletonDemo { 

private static volatile SingletonDemo instance = null;
private SingletonDemo(){ 

System.out.println(Thread.currentThread().getName() + "构造方法");
}
//DCL双端加锁机制
public static SingletonDemo getInstance(){ 

if (instance == null){ 

synchronized (SingletonDemo.class){ 

if (instance == null){ 

instance = new SingletonDemo();
}
}
}
return instance;
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/161256.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • <HTML5和CSS3响应式WEB设计指南>译者序

    <HTML5和CSS3响应式WEB设计指南>译者序

  • Nginx搭建视频点播和视频直播服务器[通俗易懂]

    Nginx搭建视频点播和视频直播服务器[通俗易懂]Nginx搭建视频点播和视频直播服务器一·、环境:Centos7,(推荐,Ubuntu不是很好用,经常会有一些莫名其妙的报错)Nginx1.10.1二、系统环境搭建首先,我是不建议自己一个个去安装这些软件的,耗时耗力,而且,容易出错,所以,最好使用yuminstall***命令安装,出错的概率小。资源链接:链接:https://pan.baidu.com/s/1WmJYpQ_b…

  • 自动化渗透测试系统_自动化测试用例管理工具

    自动化渗透测试系统_自动化测试用例管理工具一.渗透测试“三板斧”1.信息搜集——全面了解系统网络信息:DNSIP端口服务器信息:操作系统版本服务中间件;版本WEB系统信息:使用技术部署系统数据库第三方软件:版本社工记录:个人邮件地址泄露账号密码历史网站信息2.漏洞利用——占领根据地web漏洞发现系统漏洞发现漏洞利用编写自动漏洞利用脚本放置隐蔽后门3.横向扩展——扩大成果,深度挖掘内网架构分析、攻陷信息中心和数据中心、突破认证服务器(AD域)、内网中间人攻击(获取单点信息)、多级多点后

  • Pytest(16)随机执行测试用例pytest-random-order[通俗易懂]

    Pytest(16)随机执行测试用例pytest-random-order[通俗易懂]前言通常我们认为每个测试用例都是相互独立的,因此需要保证测试结果不依赖于测试顺序,以不同的顺序运行测试用例,可以得到相同的结果。pytest默认运行用例的顺序是按模块和用例命名的ASCII编码

  • hadoop hdfs命令_hadooplinux命令

    hadoop hdfs命令_hadooplinux命令概述所有的HDFS命令都调用bin/hdfs脚本文件,如果运行hdfs脚本没有带任何参数的话,则打印所有命令的描述。 使用:hdfs[SHELL_OPTIONS]COMMAND[GENERIC_OPTIONS][COMMAND_OPTIONS]Hadoop是一个参数解析框架,采用解析泛型选项以及运行类。命令参数 描述 –config–loglevel …

  • 使用Docker(k8s)安装Kafka并使用宿主机连接

    使用Docker(k8s)安装Kafka并使用宿主机连接使用Docker(k8s)安装Kafka并使用宿主机连接安装Docker及docker-compose具体安装方法可以去官网看教程检查docker-compose是否安装成功创建docker-compose.yml文件version:’2’services:zookeeper:image:”zookeeper”hostname:”zookeeper.local”container_name:”zookeeper”#设置网络别名可随

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号