首页 > 其他分享 >Volatile不保证原子性及解决方案

Volatile不保证原子性及解决方案

时间:2024-07-19 16:20:11浏览次数:11  
标签:count int 解决方案 counter 原子 ++ volatile Volatile 性及

原子性的意义

原子性特别是在并发编程领域,是一个极其重要的概念,原子性指的是一个操作或一组操作要么全部执行成功,要么全部不执行,不会出现部分执行的情况。这意味着原子性操作是不可分割的,它们在执行过程中不会被其他操作中断或干扰。

原子性的意义在于它保证了数据的一致性和程序的正确性。在多线程或多进程的环境中,当多个操作同时访问和修改共享数据时,如果没有原子性保证,可能会导致数据不一致或不确定的结果。例如,如果一个线程在读取某个数据时,另一个线程同时修改了这个数据,那么第一个线程读取到的数据可能是不正确的。通过确保操作的原子性,可以避免这种情况,从而维护数据的完整性和程序的正确执行。

了解了上面的原子性的重要概念后,接下来一起聊一聊 volatile 关键字。

volatile 关键字在 Java 中用于确保变量的更新对所有线程都是可见的,但它并不保证复合操作的原子性。这意味着当多个线程同时访问一个 volatile 变量时,可能会遇到读取不一致的问题,尽管它们不会看到部分更新的值。

Volatile 的限制

  • 不保证原子性:volatile 变量的单个读写操作是原子的,但复合操作(如自增或同步块)不是原子的。
  • 不保证顺序性:volatile 变量的读写操作不会与其他操作(如非 volatile 变量的读写)发生重排序。

一个例子

用一个示例来解释会更清楚点,假如我们有一段代码是这样的:

class Counter {
    private volatile int count = 0;

    void increment() {
        count++;
    }

    int getCount() {
        return count;
    }
}

尽管 count 是 volatile 变量,但 increment 方法中的复合操作 count++(读取-增加-写入)不是原子的。因此,在多线程环境中,多个线程可能会同时读取相同的初始值,然后增加它,导致最终值低于预期。

volatile 不保证原子性的代码验证

以下是一个简单的 Java 程序,演示了 volatile 变量在多线程环境中不保证复合操作原子性的问题:


public class VolatileTest {
    private static volatile int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        int numberOfThreads = 10000;
        Thread[] threads = new Thread[numberOfThreads];

        for (int i = 0; i < numberOfThreads; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    counter++;
                }
            });
            threads[i].start();
        }

        for (int i = 0; i < numberOfThreads; i++) {
            threads[i].join();
        }

        System.out.println("Expected count: " + (numberOfThreads * 100));
        System.out.println("Actual count: " + counter);
    }
}

在这个例子中:

  • counter 是一个 volatile 变量。
  • 每个线程都会对 counter 执行 100 次自增操作。
  • 理论上,如果 counter++ 是原子的,最终的 counter 值应该是 10000 * 100。

然而,由于 counter++ 包含三个操作:读取 counter 的值、增加 1、写回 counter 的值,这些操作不是原子的。因此,在多线程环境中,最终的 counter 值通常会小于预期值,这证明了 volatile 变量不保证复合操作的原子性。

解决方案

1. 使用 synchronized 方法或块:

  • 将访问 volatile 变量的方法或代码块声明为 synchronized,确保原子性和可见性。
class Counter {
    private volatile int count = 0;

    synchronized void increment() {
        count++;
    }

    synchronized int getCount() {
        return count;
    }
}

2. 使用 AtomicInteger 类:

java.util.concurrent.atomic 包中的 AtomicInteger 提供了原子操作,可以替代 volatile 变量。


import java.util.concurrent.atomic.AtomicInteger;

class Counter {
    private AtomicInteger count = new AtomicInteger(0);

    void increment() {
        count.incrementAndGet();
    }

    int getCount() {
        return count.get();
    }
}

3. 使用锁(如 ReentrantLock):

使用显式锁(如 ReentrantLock)来同步访问 volatile 变量的代码块。


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Counter {
    private volatile int count = 0;
    private final Lock lock = new ReentrantLock();

    void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

使用volatile变量的正确使用场景

如果操作是简单的读写,并且你只需要保证可见性,可以使用 volatile。但对于复合操作,可以使用上述其他方法来实现,通过这些方法,可以确保在多线程环境中对共享资源的正确同步和可见性。

标签:count,int,解决方案,counter,原子,++,volatile,Volatile,性及
From: https://www.cnblogs.com/wgjava/p/18311697

相关文章

  • volatile关键字
    volatile可以保证可见性,但不保证原子性:当写一个volatile变量时,JMM会把该线程在本地内存中的变量强制刷新到主内存中去;这个写操作会导致其他线程中的volatile变量缓存无效。volatile会禁止指令重排重排序需要遵守的规则:重排序不会对存在数据依赖关系的操作进行重排......
  • TinyMCE: 您的全方位富文本编辑解决方案
    核心优势和适用场景释放创意,驾驭数字世界——TinyMCE在当今数字化的时代,内容创作与呈现方式的灵活性至关重要。TinyMCE,作为一款领先的富文本编辑器,正引领着这一领域的创新潮流。无论您是专业开发者,还是日常办公人员,TinyMCE都能成为您的得力助手,让您的创作过程更加流畅、高效。......
  • 中电金信:语言服务游戏行业解决方案
    01方案概述我们的全球母语译员团队,即是资深的狂热游戏玩家,又拥有丰富的游戏本地化经验,可以针对不同平台发布的各种类型游戏,提供50+种语言的高质量一站式本地化服务,能够以完美贴切的本地化语言让全球玩家拥有同等沉浸式的游戏体验,让您的游戏作品吸引全球受众,并获得更多的忠实玩家......
  • 视频联网共享平台LntonCVS视频监控汇聚平台视频云解决方案
    LntonCVS流媒体平台是一款遵循国家GB28181标准协议的先进视频监控与云服务平台。该平台设计独特,能够同时接入并处理多路设备的视频流,支持包括RTSP、RTMP、FLV、HLS、WebRTC在内的多种视频流格式的分发。其功能丰富多样,涵盖了视频直播监控、云端录像存储、高效的云存储解决方案......
  • 【解决方案】CMT2189D系列动能芯片成功量产落地智能马桶无线遥控方案
    自发电无线遥控设备,是将手按开关或按键产生的微量动能、通过能量采集模组转化为电能供给控制器工作,再把控制信号通过无线射频发射出去控制其他设备工作。能量采集转换模组目前主要有微型发电机(闭合导线去切割磁感线)和压电生电2种方式(如下图)。前者产生的电量较多、成本高,做成的......
  • win 应用程序无法正常启动 0xc00007b的一个解决方案
    // win应用程序启动时,常常因为操作系统之间的差异出现各种问题,常见的就是动态库缺失可以使用 Dependencies分析应用程序依赖的动态库参考    Windows下查看程序依赖开源工具Dependencies使用_dependenciesgui-CSDN博客github   Releases·lucasg/Dependen......
  • uni-app的checkbox组件有些情况下视图层不更新解决方案
    应用场景问题:在使用uniapp的复选框组件checkbox实现列表的全选跟不全选功能时发现,列表的checkbox视图层在某些情况下不生效    解决方法 解决方案1:利用  this.$set改变数据,即 this.$set(item,'checked',false),这个时候视图层跟数据都一起更新了,但是在上面那种......
  • 深入解析 Spring Boot 项目中的 Maven 依赖冲突及其解决方案
    深入解析SpringBoot项目中的Maven依赖冲突及其解决方案在使用SpringBoot开发项目时,Maven作为构建工具为我们提供了极大的便利。然而,随着项目规模的扩大和依赖项的增多,依赖冲突问题也随之而来。本文将深入探讨SpringBoot项目中Maven依赖冲突的原因、检测方法以及解......
  • 整合Maven后加载不到Jar包解决方案
    简介:在整合Maven项目时,有时可能会遇到无法加载Jar包的问题。本文将提供解决此问题的方法和步骤,帮助您顺利解决加载不到Jar包的困境。当您在整合Maven项目后遇到无法加载Jar包的问题时,这通常是由于以下原因之一导致的:1、Maven依赖未正确配置:确保您的pom.xml文件中正确配置了......
  • 项目方案:视频图像结构化分析技术在车辆和人体检测中的应用方案(视频公共安全领域的解决
    目录一、视频结构化分析技术介绍1、概述2、定义3、核心环节4、应用领域二、视频中车辆和人的结构化1、需求2、信息内容3、功能说明(1)信息智能识别功能(2)智能检索功能(3)数据统计功能(4)布控报警(5)任务管理(6)资源管理(7)系统管理(8)接口管理三、应用实例1、人员参考场景......