Java内存模型与可见性:volatile关键字、内存屏障与原子操作 第二章

目录

一、volatile关键字详解

1.1 volatile关键字的定义与作用

1.2 volatile保证内存可见性的原理:禁止指令重排序与强制内存刷新

1.4 实例分析:使用volatile解决典型内存可见性问题


一、volatile关键字详解

1.1 volatile关键字的定义与作用

定义volatile是Java语言提供的一个关键字,用于修饰字段(成员变量)。当一个变量被声明为volatile时,Java内存模型(JMM)为其提供了特殊的访问规则和语义保证。

作用

  • 内存可见性volatile保证了当一个线程对volatile变量进行写操作后,所有其他线程都能够立即看到这个写操作的结果,即确保了共享变量的可见性。
  • 禁止指令重排序volatile可以阻止编译器和处理器对volatile变量相关操作进行不必要的重排序,保证程序执行的有序性。

1.2 volatile保证内存可见性的原理:禁止指令重排序与强制内存刷新

禁止指令重排序: 编译器和处理器为了优化性能,可能会对指令进行重排序。然而,对volatile变量的操作不允许被重排序到其之前的任何读写操作之前,也不允许被重排序到其之后的任何读写操作之后。这意味着对volatile变量的读写操作在多线程环境下具有明确的先后顺序,确保了其他线程能够看到正确的数据版本。

强制内存刷新: 当一个线程写入volatile变量时,该写操作不仅会更新当前线程的工作内存,还会立即将新值同步到主内存中。同时,其他线程读取volatile变量时,会跳过工作内存,直接从主内存中读取最新值。这种强制内存刷新机制确保了对volatile变量的修改能够迅速传播到所有线程,避免了由于缓存一致性问题导致的内存可见性问题。

1.3 volatile与原子性:仅能应用于特定场景,如单例模式中的双重检查锁定(DCL)

虽然volatile关键字提供了内存可见性保证,但它本身并不能保证对变量的操作是原子性的。这意味着对于复合操作(如递增、递减、非原子类型的赋值等),即使变量被声明为volatile,也不能确保这些操作在多线程环境下不会发生竞态条件。

然而,在某些特定场景下,volatile可以与原子性操作相结合,实现特定的并发安全需求。例如,在单例模式的双重检查锁定(Double Checked Locking, DCL)实现中,volatile关键字用于确保单例对象的唯一实例在多线程环境下对所有线程都是可见的。尽管volatile不能保证instance = new Singleton()这一复合操作的原子性,但由于Java对象的构造过程本身是原子的,结合volatile的内存可见性保证,可以确保其他线程看到的instance引用永远指向一个完全初始化的单例对象。

1.4 实例分析:使用volatile解决典型内存可见性问题

示例1:计数器同步问题

class Counter {
    private volatile int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        final Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                counter.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                counter.increment();
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println(counter.getCount()); // 此时结果应为20000
    }
}

示例2:状态标志同步问题

class TaskExecutor {
    private volatile boolean isTaskDone = false;

    public void executeTask() {
        // 执行耗时任务...
        isTaskDone = true; // 任务完成时设置标志
    }

    public boolean isTaskCompleted() {
        return isTaskDone;
    }
}

public class Main {
    public static void main(String[] args) {
        TaskExecutor executor = new TaskExecutor();
        new Thread(executor::executeTask).start();

        // 主线程轮询检查任务完成状态
        while (!executor.isTaskCompleted()) {
            // 等待或执行其他操作
        }

        System.out.println("Task completed!");
    }
}

在这个例子中,volatile关键字确保了当executeTask方法在其他线程中完成耗时任务并设置isTaskDonetrue时,主线程能够立即感知到这一状态变化,避免了无谓的轮询等待。

以上实例展示了volatile关键字如何解决典型的内存可见性问题,通过提供内存可见性和禁止指令重排序的特性,确保了多线程环境下共享变量状态的一致性和正确性。需要注意的是,对于需要原子性保证的复合操作,应结合使用synchronizedLock API或原子类(如AtomicInteger)等同步机制。

  • 33
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值