volatile关键字
Posted shaoyayu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了volatile关键字相关的知识,希望对你有一定的参考价值。
volatile关键字
学习材料来源于网络
如有侵权,联系删除
volatile关键字
可见性问题:让一个线程对共享变量的修改,能够及时的被其他线程看到。
根据JMM中规定的happen before和同步原则:
对某个volatile字段的写操作 happens-before每个后续对该volatile字段的读操作。对volatile变量v的写入,与所有其他线程后续对v的读同步
要满足这些条件,所以volatile关键字就有这些功能:
1.禁止缓存;
volatile变量的访问控制符会加个ACC_VOLATILE
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5
2.对volatile变量相关的指令不做重排序;
示例1
public class VisibilityDemo {
private volatile boolean flag = true;
public static void main(String[] args) throws InterruptedException {
VisibilityDemo visibilityDemo = new VisibilityDemo() ;
new Thread(new Runnable(){
@Override
public void run() {
int i = 0;
while (visibilityDemo.flag) {
i++;
}
System.out.println(i);
}
}).start() ;
TimeUnit. SECONDS. sleep( 2);
visibilityDemo.flag = false;
//设置is为false,使上面的线程结束wh ile循环demo1.flag = false;
System. out.println("被置为false了.");
}
}
final在JMM中的处理
- final在该对象的构造函数中设置对象的字段,当线程看到该对象时,将始终看到该对象的final字段的正确构造版本。伪代码示例: f= new finalDemo();读取到的f.x一定最新,x为final字段。
- 如果在构造函数中设置字段后发生读取,则会看到该final字段分配的值,否则它将看到默认值;伪代码示例: public finalDemo(){x= l;y=x;}; y会等于1;
- 读取该共享对象的final成员变量之前,先要读取共享对象。
伪代码示例:r=new ReferenceObj(); k= r.f;这两个操作不能重排序 - 通常static final是不可以修改的字段。然而System.in,System.out和System.err是static final字段,遗留原因,必须允许通过set方法改变,我们将这些字段称为写保护,以区别于普通final字段;
Word Tearing字节处理
一个字段或元素的更新不得与任何其他字段或元素的读取或更新交互。
特别是,分别更新字节数组的相邻元素的两个线程不得干涉或交互,也不需要同步以确保顺序一致性。
有些处理器(尤其是早期的Alphas处理器)没有提供写单个字节的功能。
在这样的处理器上更新 byte数组,若只是简单地读取整个内容,更新对应的字节,然后将整个内容再写回内存,将是不合法的。
这个问题有时候被称为“字分裂(word tearing)”,在单独更新单个字节有难度的处理器上,就需要寻求其它方式了。
基本不需要考虑这个,了解就好。
public class WordTearing extends Thread {
static final int LENGTH = 8;
static final int ITERS = 1000000;
static byte[] counts = new byte[LENGTH];
static Thread[] threads = new Thread[LENGTH];
final int id;
WordTearing(int i) {
id = i;
}
public void run() {
byte v = 0;
for (int i = 0; i < ITERS; i++) {
byte v2 = counts[id];
if (v != v2) {
System.err.println("Word-Tearing found: " +
"counts[" + id + "] = "+ v2 +
", should be " + v);
return;
}
v++;
counts[id] = v;
}
}
public static void main(String[] args) {
for (int i = 0; i < LENGTH; ++i)
(threads[i] = new WordTearing(i)).start();
}
}
double和long的特殊处理
虚拟机规范中,写64位的double和long分成了两次32位值的操作
由于不是原子操作,可能导致读取到某次写操作中64位的前32位,以及另外一次写操作的后32位
读写volatile 的 long和double总是原子的。读写引用也总是原子的
商业JVM不会存在这个问题,虽然规范没要求实现原子性,但是考虑到实际应用,大部分都实现了原子性。
以上是关于volatile关键字的主要内容,如果未能解决你的问题,请参考以下文章