Java多线程并发编程 Volatile关键字

网友投稿 203 2023-05-13


Java多线程并发编程 Volatile关键字

volatile 关键字是一个神秘的关键字,也许在 J2EE 上的 java 程序员会了解多一点,但在 android 上的 JAVA 程序员大多不了解这个关键字。只要稍了解不当就好容易导致一些并发上的错误发生,例如好多人把 volatile 理解成变量的锁。(并不是)

volatile 的特性:

具备可见性

保证不同线程对被 volatile 修饰的变量的可见性。

有一被 volatile 修饰的变量 i,在一个线程中修改了此变量 i,对于其他线程来说 i 的修改是立即可见的。

如:

volatile int i = 0;// 语句 1

i++; // 语句 2

语句 2 执行完后,i 最新的值会立即被强制更新到主内存(共享内存),并通知其他缓存了 i 的线程,令其他线程的工作内存里的 i 失效,从而需重新到主内存读取最新的值。

具备有序性

被 volatile 修饰的变量,不会被优化排序。

解决的问题详见:Java 多线程并发编程 并发三大要素 的 三、有序性。

当编译器在给程序优化排序时,若遇到 volatile 变量的读操作或者写操作,则会保证在其前面的操作全部进行完成,且结果对后面的操作可见;并且保证在其后面的操作没有进行。

不具备原子性

volatile 不具备原子性,所以它是线程不安全的。

实验:

// 一个单例的实现

public class SingletonTest {

private static volatile SingletonTest mInstance =http:// null;

private SingletonTest() {}

public static SingletonTest getInstance() {

if (mInstance == null) {

mInstance = new SingletonTest();

System.out.println(" 初始化完成 ");

}

return mInstance;

}

}

// 测试代码

public class Test {

public static void main(String[] var0) {

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

ThreadTest test = new ThreadTest();

test.start();

}

}

static class ThreadTest extends Thread{

@Override

public void run() {

super.run();

SingletonTest.getInstance();

}

}

}

结果:

每次运行都输出多个 “初始化完成”。

volatile 的解释

下面这段话摘自《深入理解 Java 虚拟机》:

“http://观察加入 volatile 关键字和没有加入 volatile 关键字时所生成的汇编代码发现,加入 volatile 关键字时,会多出一个 lock 前缀指令”

被 volatile 修饰的变量进行读和写操作的时候,在相应的汇编程序中都会多一句内存屏障(Memory Barrier)。

而这个 lock 就是内存屏障。

内存屏障的作用:

1、在重新优化排序时保证其后面的指令不会被排到内存屏障的前面,前面的指令也不会排到内存屏障的后面。- 有序性

2、强制对写操作后的结果(立即)刷新到主内存。

3、刷新结果到主内存时,通知并令其他线程缓存内的值过期 / 失效。

2 和 3 合起来则是可见性。

说到这里,也许会有好多人困惑,既然可见性可以保证,既然可以做到修改某个变量的值后,会刷新到主内存,并令其他线程缓存失效,为什么不能保证原子性呢?这也是我之前走进的一个困区。

继续用 i++ 来分析一下,这里面包含的指令:

从主内存读取到缓存 // 指令 1

进行运算 // 指令 2

从缓存刷新到主内存 // 指令 3

内存屏障 // 指令 4

虽然指令 4(内存屏障)功能强大,但可惜 // 指令 1、2、3 都不是具备原子性,所以导致 volatile 不具备原子性,线程不安全,不能替代锁的作用。

使用场景

如一些简单的状态标记:

YojZPnwFE

volatile boolean inited = false;

// 线程 1

init(); // 语句 1

inited = true; // 语句 2

// 线程 2

while(inited){

work(); // 语句 3

}

1、可确保语句 1 和语句 2 的执行顺序。

2、可确保执行语句 2 后,线程 2 可立即获取到最新的修改,从而执行语句 3。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java创建内部类对象实例详解
下一篇:网络接口卡实现的功能(网络接口卡的基本功能包括)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~