一,简介
jdk中提供了volatile关键字,用于修饰变量。提供了两层语义:
语义一:保证共享变量内存的可见性(并不能保证操作的原子性)。
语义二:禁用指令的重排序。
二,内存可见性原理分析
在Java内存模型中,内存分为主内存(堆内存)和工作内存(栈内存)两个部分,其中主内存是所有线程所共享的,而工作内存则是每个线程分配一份,各线程的工作内存彼此间独立、互不可见,在线程启动的时候,虚拟机为 每个线程分配一块工作内存,不仅包含了线程内部定义的局部变量,还包含了线程所需要使用的共享变量(非线程内构造的对象)的副本,即为了提高执行效率,读取副本比直接读取主内存更快,栈是连续的小空间、顺序入栈出栈,而堆是不连续的大空间,所以在栈中寻址的速度比堆要快很多)。各工作内存之间数据的交换通过主内存来进行的,如下图:
共享变量在工作内存中发生变化了之后,必须要写回到主内存中(迟早要写回但并非马上写回),但对于volatile关键字修饰的变量则要求工作内存中发生变化之后,必须马上写回到主内存,而线程每次要使用volatile修饰的共享变量时,都会直接到主内存中获取最新的值(而不是读取工作内存中的副本)。从而实现所有线程对该共享变量变化的可见性。
但volatile仅能保证变量的可见性,并不能保证操作的原子性:
假如线程A在做了i+1,但未赋值的时候,线程B就开始读取i,那么当线程A赋值i=1,并立即回写到主内存,而此时线程B已经不再需要i的值了,而是直接交给处理器去做+1的操作,于是当线程B执行完并回写到主内存,i的值仍然是1,而不是预期的2。
三,禁用指令重排序
synchronized、Lock以及volatile修饰符都可以禁用指令的重排序。
单例模式中双重检测机制:
private static volatile StoreKeeper instance = null; private StoreKeeper(){//对象初始化 } public static StoreKeeper getInstance(){ if(null == instance){ synchronized (StoreKeeper.class) { if(null == instance){ instance = new StoreKeeper(); } } } return instance; }
问题原因:
当a线程进入getinstance()方法,判断instance是null,然后执行instance = new StoreKeeper();但是实例化一个对象内部是很复杂的,有好多动作:
1.给这个对象分配空间
2.执行构造函数给成员赋值
3.将这个对象空间地址赋给instance变量
而根据java内存模型,在单个线程中,只要重排序不会对结果产生影响,那么就不能保证其中的操作一定按照程序写定的顺序执行---即使重排序对于其他线程来说会产生明显的影响,java存储模型允许编译器重排序操作,在寄存器中缓存数值,还允许cpu重排序,并在处理器特有的缓存中缓存数值,那么就是说线程a去实例化这个对象的时候可能是按照1-2-3的顺序执行,也可能是1-3-2的顺序执行
可能原因:如果是按照1-3-2执行顺序,那么当a线程执行完3这步的时候,cpu调度,这个时候b线程进来,判断instance是否为null,而此时instance肯定不为null,所以b线程就直接返回了这个对象
但是此时这个对象的所有成员变量还没有初始化成指定的值呢,因为线程a还没有来得及给成员变量赋值
构造方法还没有执行完,当b线程调用这个对象进行操作会产生无法预计的后果。
所以修改的方法是增加volatile关键字:private static volatile StoreKeeper instance;
相关推荐
java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java volatile 关键字实战java ...
### Java并发编程:volatile关键字解析 #### 一、内存模型的相关概念 在深入了解`volatile`关键字之前,我们首先需要理解计算机内存模型的一些基本概念。在现代计算机系统中,CPU为了提高执行效率,会将频繁访问的...
一个定义为volatile 的变量是说这变量可能会被意想不到地改变,这样,编 译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必 须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里...
在Java并发编程中,volatile关键字是一种轻量级的同步机制,它用于确保变量的可见性和有序性。本文将详细探讨volatile关键字的工作原理、使用场景以及如何在实际开发中正确使用volatile。 volatile关键字是Java并发...
volatile关键字的作用是提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,...
volatile关键字的主要作用是提供对特殊地址的稳定访问,防止编译器对访问该变量的代码进行优化。 volatile关键字的作用是告诉编译器,变量的值可能会被外部因素改写,因此编译器不能对访问该变量的代码进行优化。...
本篇文章将深入探讨volatile关键字的作用,并结合GD32F303单片机的使用场景,来阐述它在实际开发中的应用。 首先,volatile关键字是C/C++语言中的一个修饰符,它的主要功能是告诉编译器,该变量的值可能会在编译器...
"Java中的Volatile关键字详解" Java中的Volatile关键字详解是Java中的一种关键字,用于保证线程之间的可见性、原子性和有序性。下面是对Java中的Volatile关键字详解的知识点总结: 一、基本概念 1. 可见性:可见...
深入理解volatile关键字 volatile关键字是Java语言的高级特性,它可以保证可见性和禁止指令重排序,但是要弄清楚其工作原理,需要先弄懂Java内存模型。 保证可见性 volatile关键字可以保证可见性,即当一个线程...
除了`volatile`之外,`const`关键字在嵌入式编程中也有着重要的作用。通过合理使用`const`,不仅可以提高代码的可读性,还能帮助优化器生成更加高效、安全的代码。 #### 基本概念 `const`关键字用于声明一个变量或...
以前我们说过在一些简单的例子中,比如为一个字段赋值或递增该字段,我们需要对线程进行同步,虽然lock可以满足我们的需要,但是一个竞争锁一定会导致阻塞,然后忍受线程上下文切换和调度的开销,在一些高并发和性能...
java volatile 关键字 学习
volatile关键字的作用 volatile关键字是Java、C、C++等编程语言中的一个特殊关键字,它的主要作用是限定变量在多个线程或进程间的可见性和修改顺序。下面我们将详细介绍volatile关键字的作用和应用场景。 1. 防止...
" Java并发编程volatile关键字的作用 " Java并发编程中的volatile关键字是Java语言中提供的一种轻量级的同步机制,主要用于实现可见性和禁止指令重排。volatile关键字可以使变量在多线程之间可见,确保变量的最新值...
Java中volatile关键字实现原理 volatile关键字是Java语言中的一种机制,用于保证变量在多线程之间的可见性。它是Java.util.concurrent包的核心,没有volatile就没有那么多的并发类供我们使用。本文详细解读一下...
Java中的`volatile`关键字是一个非常重要的并发编程工具,它的作用主要体现在两个方面:**可见性**和**有序性**。本文将深入解析`volatile`的关键字特性及其在实际编程中的应用。 1. 可见性: 当一个共享变量被`...
Java并发编程中,volatile关键字扮演着重要的角色,它是一种轻量级的同步机制,与synchronized相比,volatile在性能上更优,因为它不会导致线程阻塞。在深入理解volatile的关键特性之前,我们需要先了解Java内存模型...
Java中的`volatile`关键字是多线程编程中的一个重要概念,它的主要作用是确保共享变量的可见性和禁止指令重排序。本文将深入探讨`volatile`的关键特性、工作原理以及使用注意事项。 1. 可见性: `volatile`关键字...
当涉及到多任务处理或中断服务例程(ISR)时,`volatile` 关键字的作用变得尤为关键。这是因为这些情况下变量的值可能在任何时候被外部进程或者中断服务例程所改变。如果不恰当地使用 `volatile`,编译器可能会做出...
Java中的`volatile`关键字是一个非常重要的并发编程工具,它的主要作用是确保共享变量在多线程环境下的可见性和有序性。下面将详细解释`volatile`的关键特性、它如何解决并发问题以及相关的`happens-before`原则。 ...