volatile关键字使用场景举例

网上讲volatile的很多,大多都是从定义和内存模型上来介绍,本文就举两个例子来说下我的心得吧

1.状态标志位:

volatile字段常用于多线程环境下的状态标志位。例如,一个线程修改了某个标志位的值,其他线程可以立即看到最新的值,从而做出相应的处理。在这里,我们举个简单的例子。假设我们有一个MyThread类,该类包含一个isRunning字段和一个run方法,如下所

在上述示例中,一个线程通过设置isRunning字段为false来停止另一个线程的执行。由于isRunning字段被声明为volatile,其他线程在访问该字段时能够立即看到最新的值,从而及时停止执行。如果中没有使用volatile修饰isRunning字段,那么其他线程可能无法立即看到该字段的最新值,从而继续执行循环,导致无法正常停止。(可能会多运行一段时间) 使用volatile关键字修饰isRunning字段可以解决这个问题。volatile关键字保证了字段的可见性,即当一个线程修改了isRunning字段的值时,其他线程能够立即看到最新的值,从而及时停止执行。

2.双重检查锁-单例

在这个例子中,如果不使用volatile修饰instance字段,可能会导致多线程环境下的线程安全问题。 当多个线程同时调用getInstance()方法时,如果instance字段没有被volatile修饰,那么就可能存在以下情况: 线程A检查到 instance 在线程A创建实例的过程中,另一个线程B也检查到 instance synchronized 当线程A完成实例的创建并释放锁之后,线程B获得锁,然后也开始创建实例。 线程B创建出来的实例会覆盖掉线程A创建的实例,从而导致单例模式失效。

3.防止指令重排,还是上面的例子

举个例子来说明,假设有两个线程A和B,它们都通过getInstance()方法获取单例对象。在某一时刻,线程A执行到如下代码:

instance = new Singleton();

按照正常的顺序,这行代码应该依次完成分配内存空间、初始化对象、将引用赋值给变量的操作。然而,由于指令重排优化,编译器和处理器可能会改变执行顺序,将赋值操作(将引用赋值给变量)提前到内存空间分配和对象初始化之前。这样,在线程A执行完赋值操作之前,线程B就已经检查到instance不为null,然后开始使用这个对象。

如果此时线程B访问了未完全初始化的对象,就可能会出现问题。因为对象的初始化可能包含一些关键的操作,例如为实例变量赋初值或连接数据库等。如果线程B使用了未完全初始化的对象,就会出现数据不一致或其他错误。

而如果我们使用volatile关键字修饰了instance字段,就能够防止指令重排优化。具体而言,对于被volatile修饰的字段,编译器和处理器会确保读写操作按照程序的先后顺序执行,从而保证了对象的完全初始化。这样,在一个线程完成实例创建后,其他线程在获取instance时能够立即看到最新的值,避免了另一个线程获取到未完全初始化的对象。

总之,没有volatile关键字修饰的情况下,编译器和处理器可能会对指令进行重新排序优化,导致另一个线程获取到未完全初始化的对象。而使用volatile关键字修饰可以防止指令重排,确保对象的完全初始化,并保证多线程环境下的正确性和可见性。

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值