volatile关键字三重功效

2024-06-18 08:48

本文主要是介绍volatile关键字三重功效,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  1. 64位写入原子性

举一个简单的例子,对于一个long型变量的赋值和取值操作而言,在多线程场景下,线程A调用set(100),线程B调用get(),在某些场景下,返回值可能不是100。

package com.lc.test02;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** @author liuchao* @date 2020/7/3*/
public class Test01 {private long a = 0;public void set(long a) {//线程1设置值this.a = a;}public long get() {//线程2获取值,返回值可能不是100return a;}public static void main(String[] args) {Test01 test = new Test01();ExecutorService executor = Executors.newFixedThreadPool(2);CountDownLatch latch = new CountDownLatch(1);executor.execute(() -> {try {latch.await();} catch (InterruptedException e) {e.printStackTrace();}long i = 100L;test.set(i);System.out.println("-----设置:" + i);});executor.execute(() -> {try {latch.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("----获取:" + test.get());});latch.countDown();executor.shutdown();}}

 为什么获取到的值有可能不是100呢,是因为JVM规范并未要求64位的long和double写入是原子性的,64位的数据有可能被拆分成两个32位的数据写入,这样另一个线程拿到的数据有可能是32位的不完整的数据,如果在long属性前面加上volatile关键字就可以解决此问题。

  1. 内存可见性

JVM将内存组织为主内存和工作内存两个部分。

针对主内存中的变量,线程A操作后线程B看到要经过的流程

线程A操作后数据存储在线程A工作内存=》save到主内存=》线程B从主内存load到线程B工作内存

那在整个操作过程中是非原子性操作的,有可能线程A修改后是10,但是线程B读取到的数据是非10,因为线程A修改后的数据还未save到主内存,那要解决这个问题也比较简单就是直接在属性前面加上volatile关键字,也就是解决了内存可见性问题 

  1. 禁止重排序

在经典的单线程安全的写法上DCL

//注意,此代码有安全问题package com.lc.test02;/*** @author liuchao* @date 2020/7/3*/
public class Test01 {private static Test01 instance;public static Test01 getInstance() {if (null == instance) {synchronized (instance) {//为了性能验证 使用lockif (null == instance) {instance = new Test01();//有问题的代码}}}return instance;}}

上述的instance = new Test01();代码有问题:其底层会分为三个操作:

(1)分配一块内存。

(2)在内存上初始化成员变量。

(3)把instance引用指向内存。 

在这三个操作中,操作(2)和操作(3)可能重排序,即先把instance指向内存,再初始化成员变量,因为二者并没有先后的依赖关系。此时,另外一个线程可能拿到一个未完全初始化的对象。这时,直接访问里面的成员变量,就可能出错。这就是典型的“构造函数溢出”问题。解决办法也很简单,就是为instance变量加上volatile修饰

 

这篇关于volatile关键字三重功效的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1071795

相关文章

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

Python批量替换多个Word文档的多个关键字的方法

《Python批量替换多个Word文档的多个关键字的方法》有时,我们手头上有多个Excel或者Word文件,但是领导突然要求对某几个术语进行批量的修改,你是不是有要崩溃的感觉,所以本文给大家介绍了Py... 目录工具准备先梳理一下思路神奇代码来啦!代码详解激动人心的测试结语嘿,各位小伙伴们,大家好!有没有想

Java中的volatile关键字多方面解析

《Java中的volatile关键字多方面解析》volatile用于保证多线程变量可见性与禁止重排序,适用于状态标志、单例模式等场景,但不保证原子性,相较synchronized更轻量,但需谨慎使用以... 目录1. volatile的作用1.1 保证可见性1.2 禁止指令重排序2. volatile的使用

C#中lock关键字的使用小结

《C#中lock关键字的使用小结》在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块,下面就来介绍一下lock关键字的使用... 目录使用方式工作原理注意事项示例代码为什么不能lock值类型在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

Java 关键字transient与注解@Transient的区别用途解析

《Java关键字transient与注解@Transient的区别用途解析》在Java中,transient是一个关键字,用于声明一个字段不会被序列化,这篇文章给大家介绍了Java关键字transi... 在Java中,transient 是一个关键字,用于声明一个字段不会被序列化。当一个对象被序列化时,被

C#中async await异步关键字用法和异步的底层原理全解析

《C#中asyncawait异步关键字用法和异步的底层原理全解析》:本文主要介绍C#中asyncawait异步关键字用法和异步的底层原理全解析,本文给大家介绍的非常详细,对大家的学习或工作具有一... 目录C#异步编程一、异步编程基础二、异步方法的工作原理三、代码示例四、编译后的底层实现五、总结C#异步编程

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

Java的volatile和sychronized底层实现原理解析

《Java的volatile和sychronized底层实现原理解析》文章详细介绍了Java中的synchronized和volatile关键字的底层实现原理,包括字节码层面、JVM层面的实现细节,以... 目录1. 概览2. Synchronized2.1 字节码层面2.2 JVM层面2.2.1 ente

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭