volatile变量类型的详细说明

本文详细探讨了Java中的volatile关键字,它提供了轻量级的同步机制。文章指出,volatile确保了变量的可见性,但并不保证原子性,因此在并发情况下仍可能存在安全性问题。通过volatile、synchronized和final三种方式可以实现可见性。示例代码展示了volatile在并发计数场景下的不安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关键字volatile是java虚拟机提供的最轻量级的同步机制。但是许多人对它的理解还是不正确,不完整。

下面详细讲解一下:

由于关键字volatile关键没有被更好的理解,因此许多程序员遇到处理多线程数据竞争问题的时候都是有synchronzied来同步,仅为读写一两个实例域就使用同步,显得开销有些大了。因此需要深入理解此关键字。

Java内存模型对volatile专门定义了一些特殊的访问规则

当一个变量被定义为volatile时,此变量具备两种特性:

1.可见性:可见性在java内存模型中有定义,可以参看。

普通变量则没有,他们在线程之间的交互是通过主内存来完成,volatile变量则是通过主内存完成交换,但是两者区别在于volatile变量能立即同步到主内存中,当一个线程修改变量的变量的时候,立刻会被其他线程感知到。

特别注意一点:volatile变量的可见性经常性被误解,认为,valotile变量在各个线程中是一致的。所以基于volatile变量是安全的。这种认为是错误的。论据是正确的,但是得出的是安全的就不正确了。不会存在不一致性问题(在各个的工作内存中可以存在不一致的情况,但是由于每次使用之前都要刷新,执行引擎看不到不一致的问题,因此认为不存在不一致的问题)但是java里面的运算中并非原子操作,导致volatile变量的运算在并发下一样不安全。


实现可见性方式: 1.volatile   2.synchronized    3.final


下面代码演示不安全的情况:

public class VolatileTest {

private static final int THREAD_NUM =20;

public static volatile int num= 0;

public static void increase(){

num++ ;

}

public static void main(String[] args) {

Thread[] threads = new Thread[THREAD_NUM];

for (int i = 0; i < THREAD_NUM; i++) {

threads[i] = new Thread(new Runnable() {

@Override

public void run() {

for(int i=0;i<1000;i++){

increase();

}

}

});

threads[i].start();

}

while(Thread.activeCount()>1){

Thread.yield();

}

System.out.println(num);

}

}

输出的正确答案应该是200000,但是每次输出都小于200000,问题在于increase()方法。我们用javap发编译看一下发现就increase()方法在Class中文件有四条字节码组成。但是这种字节码测试并不能说明这条指令是原子操作,因为一条字节码指令解释执行时,解释器将要运行许多行代码才能实现它的语义,当编译执行时,一条也可能被住转化为若干本地机器指令。

当不符合一下规则是还是使用synchronized或java.util.concurrent中的原子类。
1.运算结果并不依赖变量的当前值,或者能够确保单一的线程修改变量的

2.变量不需要与其他的状态变量共同参与不变约束。

2.禁止指令重排序优化

普通变量仅仅会保证在该方法的执行过程中所依赖复制结果的地方都能获取到正确的结果,而不能保证变量复制操作的顺序与程序代码中的执行顺序一致。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值