博客
关于我
Java线程:volatile关键字
阅读量:213 次
发布时间:2019-02-28

本文共 2818 字,大约阅读时间需要 9 分钟。

Java多线程编程中,Volatile变量是一种轻量级的同步机制,与Synchronized块相比,它的编写成本更低,同时也有更高的性能表现。尽管如此,Volatile变量的使用也需要谨慎,不能用来替代所有的线程安全问题。以下将从基础到高级模式,详细探讨Volatile变量的正确使用方法及其适用场景。

为什么需要使用Volatile变量?

在计算机系统中,任何变量的读写操作都需要经过多个步骤才能完成。例如,简单的i++操作实际上包含读取、修改和写入三个步骤。对于长类型变量,赋值操作可能需要两次写入操作(低32位和高32位)。在多线程环境下,若一个线程完成了操作的部分步骤,而其他线程在这个过程中读取了变量的值,就会导致读取脏数据(dirty reading)的问题。

Volatile变量的主要作用是确保在多线程环境下,变量的读写操作能够看到最新的值,避免了上述问题。

Volatile变量的正确使用条件

Volatile变量只能在以下两种情况下提供有效的线程安全:

  • 写操作不依赖于当前值:这意味着变量的赋值操作不会受到当前值的影响。例如,简单的赋值操作或原子性操作(如i++)是可以使用Volatile变量的。但如果变量的写操作依赖于其当前值(如互斥锁),则必须使用Synchronized机制。

  • 变量的状态独立于其他变量:如果变量的状态与其他变量存在约束关系(如上下界),则无法仅通过Volatile变量确保线程安全。在这种情况下,仍需使用Synchronized机制。

  • Volatile变量的性能优势

    相比Synchronized机制,Volatile变量的主要优势在于其低开销和轻量级特性。在大多数现代处理器架构上,Volatile变量的读操作几乎没有额外的性能消耗,而写操作虽然比非Volatile写操作多一些开销,但总体而言其性能仍优于Synchronized锁的获取和释放操作。此外,Volatile变量不会导致线程阻塞,从而提高了系统的可扩展性。

    Volatile变量的正确使用模式

    在实际开发中,Volatile变量可以通过以下几种模式安全地使用:

    模式1:状态标志

    这种模式通常用于标记某个一次性事件的完成状态,例如程序的停机请求。通过设置一个Volatile布尔标志,可以避免复杂的同步逻辑,同时确保状态的可见性。例如:

    volatile boolean shutdownRequested = false;public void shutdown() {    shutdownRequested = true;}public void doWork() {    while (!shutdownRequested) {        // 做一些事情...    }}

    这种模式的核心在于状态标志的转换是原子性的,且只允许一次状态转换,因此非常适合使用Volatile变量。

    模式2:一次性安全发布

    在缺乏同步的情况下,对象引用可能会出现不一致的读取问题(如双重检查锁定问题)。通过将对象引用声明为Volatile类型,可以确保对象在发布时的可见性。例如:

    public class BackgroundLoader {    public static volatile SomeObject theObject;}public class SomeClass {    public void doSomething() {        if (BackgroundLoader.theObject != null) {            // 使用theObject...        }    }}

    这种模式的前提是被发布的对象必须是线程安全的或不可变的,否则仍需额外的同步。

    模式3:独立观察

    这种模式用于定期发布观察结果供程序内部使用。例如,背景线程定期读取环境传感器数据并存储在Volatile变量中,其他线程可以随时读取最新值:

    public class SensorReader {    public static volatile float temperatureValue = Float.NaN;    private SensorReader() {        // 初始化 sensor 并开始读取数据...    }    public void readTemperature() {        temperatureValue = sensor读到的温度值;    }}

    这种模式的前提是被发布的值必须是有效的且不可变的。

    模式4:Volatile bean模式

    这种模式通常用于JavaBean组件,确保数据成员的可见性和一致性。所有数据成员都声明为Volatile类型,且getter和setter方法不能包含任何逻辑:

    public class PersonBean {    private volatile String firstName;    private volatile String lastName;    private volatile int age;    public String getFirstName() {        return firstName;    }    public String getLastName() {        return lastName;    }    public int getAge() {        return age;    }}

    这种模式的前提是JavaBean的状态必须是线程安全的或不可变的。

    高级模式:读写锁策略

    在某些情况下,结合使用Volatile变量和Synchronized机制可以实现低开销的读写锁策略。例如,线程安全的计数器可以通过以下方式实现:

    public class CheesyCounter {    private volatile int value;    public synchronized int increment() {        return value++;    }    public int getValue() {        return value;    }}

    这种模式在读操作频繁、写操作少的情况下,能够显著降低同步开销。

    结论

    Volatile变量是一种强大的工具,但其使用必须谨慎。在大多数情况下,仅使用Volatile变量无法替代Synchronized机制,且容易出错。通过遵循上述模式,可以在不违反Volatile变量使用条件的情况下,安全地实现线程安全。同时,结合使用Volatile变量和Synchronized机制,可以在性能和可扩展性之间找到平衡点。

    转载地址:http://zyrs.baihongyu.com/

    你可能感兴趣的文章
    npm安装教程
    查看>>
    npm报错Cannot find module ‘webpack‘ Require stack
    查看>>
    npm报错Failed at the node-sass@4.14.1 postinstall script
    查看>>
    npm报错File to import not found or unreadable: @/assets/styles/global.scss.
    查看>>
    npm报错unable to access ‘https://github.com/sohee-lee7/Squire.git/‘
    查看>>
    npm版本过高问题
    查看>>
    npm的安装和更新---npm工作笔记002
    查看>>
    npm的常用配置项---npm工作笔记004
    查看>>
    npm的问题:config global `--global`, `--local` are deprecated. Use `--location=global` instead 的解决办法
    查看>>
    npm编译报错You may need an additional loader to handle the result of these loaders
    查看>>
    npm设置淘宝镜像、升级等
    查看>>
    npm配置安装最新淘宝镜像,旧镜像会errror
    查看>>
    npm错误 gyp错误 vs版本不对 msvs_version不兼容
    查看>>
    npm错误Error: Cannot find module ‘postcss-loader‘
    查看>>
    npm,yarn,cnpm 的区别
    查看>>
    NPOI之Excel——合并单元格、设置样式、输入公式
    查看>>
    NPOI初级教程
    查看>>
    NPOI利用多任务模式分批写入多个Excel
    查看>>
    NPOI在Excel中插入图片
    查看>>
    NPOI将某个程序段耗时插入Excel
    查看>>