Java基础-关键字:volatile

简介: 观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现;加入volatile关键字时,会多出一个lock前缀指令。lock前缀指令实际上相当于一个内存屏障。内存屏障会提供3个功能:确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面。强制将对缓存的修改操作立即写入主存。如果是写操作,它会导致其他CPU中对应的缓存行无效。

基础性问题

1、缓存一致性问题
程序运行,临时数据存放主存(物理内存)当中
CPU执行速率 远大于 数据在内存中读写速度
因此CPU里面就有了高速缓存
多个线程访问的变量为共享变量,存在缓存一致性问题

2、硬件层面解决方法:总线加LOCK#锁的方式、缓存一致性协议
总线加LOCK#锁的方式
对总线加锁,阻塞其他CPU对内存的访问,只能又一个CPU访问这个内存变量。
缺点:锁总线期间,其他CPU无法访问内存,导致效率地下。

缓存一致性协议
Intel的MESI协议:保证每个缓存中使用的共享变量的副本是一致的。

3、并发编程的三个基本概念:原子性,可见性,有序性
原子性:一个操作或者多个操作 要么全部执行,要么就都不执行。
可见性:多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
有序性:程序执行的顺序按照代码的先后顺序执行。

指令重排序:不会影响单个线程的执行,但是会影响到线程并发执行的正确性。
并发程序要正确地执行,必须要保证原子性、可见性以及有序性。任一条件没有被保证,就有可能会导致程序运行不正确。

Java内存模型

Java内存模型存在:缓存一致性问题 和 指令重排序问题。

所有的变量都是存在主存当中(类似于物理内存)
每个线程都有自己的工作内存(类似于高速缓存)
线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。
并且每个线程不能访问其他线程的工作内存。

原子性:
对基本数据类型的变量的读取和赋值操作是原子性操作
要实现更大范围操作的原子性,可以通过synchronized和Lock来实现

可见性:
Java提供了volatile关键字来保证可见性。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存;
当有其他线程需要读取时,它会去内存中读取新值。
普通的共享变量不能保证可见性。通过synchronized和Lock也能够保证可见性。

有序性:
在Java内存模型中,允许编译器和处理器对指令进行重排序;
重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
在Java里面,可以通过volatile关键字来保证一定的“有序性”。
可以通过synchronized和Lock来保证有序性,他保证每个时刻是有一个线程执行同步代码。

关键字:volatile : 保证可见性,不保证原子性,一定程度上保证有序性

1、保证可见性
示例代码如下:

public volatile static int inc = 1;
public static void main(String[] args) {
    new Thread() {
        public void run() {
            while(inc == 1) { }
        }
    }.start();
    
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) { }
    inc = 0;
    System.err.println("main is Over ... " + inc);
}
AI 代码解读

不加关键字volatile的时候:主线程修改变量,不会更新到子线程;子线程不会退出。
加入关键字,主线程更新到主存,子线程也去主存读取;子线程会退出。

2、不保证原子性
示例代码如下:

public static volatile int inc = 0;
public static void increase() { inc++; }
public static void main(String[] args) {
    for(int i = 0; i < 10; i++) {
        new Thread() {
            public void run() {
                for(int j = 0; j < 1000; j++) {
                    increase();
                }
            }
        }.start();
    }
    while(Thread.activeCount() > 1) Thread.yield();
    System.err.println(inc);
}
AI 代码解读

10个线程,每个线程对变量自加1,自加1000次;最终结果应该是:10000。
但是最终结果总是小于10000 。

如何保证原子性 ?
答:
(1)对方法 increase 同步锁:synchronized , 输出结果为: 10000 。
(2)定义锁 Lock ,更新方法 increase,输出结果为: 10000 。
如下代码:

public static Lock lock = new ReentrantLock();
public static void increase() { 
    lock.lock();
    try {
        inc++; 
    } finally {
        lock.unlock();
    }
}
AI 代码解读

(3)采用原子性的包装对象: AtomicInteger。 如下代码:

public static volatile AtomicInteger inc = new AtomicInteger();
public static void increase() {
    inc.getAndIncrement();
}
AI 代码解读

在java 1.5的java.util.concurrent.atomic包下提供了一些原子操作类
对基本数据类型的 自增,自减、以及加法操作,减法操作 进行了封装
atomic是利用CAS来实现原子性操作的(Compare And Swap)
CAS实际上是利用处理器提供的CMPXCHG指令实现的

3、一定程度上保证有序性
volatile关键字能禁止指令重排序,所以volatile能在一定程度上保证有序性

观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现;
加入volatile关键字时,会多出一个lock前缀指令
lock前缀指令实际上相当于一个内存屏障
内存屏障会提供3个功能:
确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面。
强制将对缓存的修改操作立即写入主存。
如果是写操作,它会导致其他CPU中对应的缓存行无效。

目录
打赏
0
0
0
0
52
分享
相关文章
|
2天前
|
Volatile关键字与Java原子性的迷宫之旅
通过合理使用 `volatile`和原子操作,可以在提升程序性能的同时,确保程序的正确性和线程安全性。希望本文能帮助您更好地理解和应用这些并发编程中的关键概念。
33 21
Java中的this关键字详解:深入理解与应用
本文深入解析了Java中`this`关键字的多种用法
150 9
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
利用 Java 代码获取淘宝关键字 API 接口
在数字化商业时代,精准把握市场动态与消费者需求是企业成功的关键。淘宝作为中国最大的电商平台之一,其海量数据中蕴含丰富的商业洞察。本文介绍如何通过Java代码高效、合规地获取淘宝关键字API接口数据,帮助商家优化产品布局、制定营销策略。主要内容包括: 1. **淘宝关键字API的价值**:洞察用户需求、优化产品标题与详情、制定营销策略。 2. **获取API接口的步骤**:注册账号、申请权限、搭建Java开发环境、编写调用代码、解析响应数据。 3. **注意事项**:遵守法律法规与平台规则,处理API调用限制。 通过这些步骤,商家可以在激烈的市场竞争中脱颖而出。
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
2月前
|
Java volatile关键字:你真的懂了吗?
`volatile` 是 Java 中的轻量级同步机制,主要用于保证多线程环境下共享变量的可见性和防止指令重排。它确保一个线程对 `volatile` 变量的修改能立即被其他线程看到,但不能保证原子性。典型应用场景包括状态标记、双重检查锁定和安全发布对象等。`volatile` 适用于布尔型、字节型等简单类型及引用类型,不适用于 `long` 和 `double` 类型。与 `synchronized` 不同,`volatile` 不提供互斥性,因此在需要互斥的场景下不能替代 `synchronized`。
2320 3
java中的this关键字
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。自学前端2年半,正向全栈进发。若我的文章对你有帮助,欢迎关注,持续更新中!🎉🎉🎉
74 9
java中的static关键字
欢迎来到瑞雨溪的博客,博主是一名热爱JavaScript和Vue的大一学生,致力于全栈开发。如果你从我的文章中受益,欢迎关注我,将持续分享更多优质内容。你的支持是我前进的动力!🎉🎉🎉
70 8
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
66 4
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
141 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等