我是靠谱客的博主 等待面包,这篇文章主要介绍Java中写双重检查加锁的单例时volatile关键字作用,现在分享给大家,希望可以做个参考。

1.保证多进程情况下变量的可见性
(1)可见性是java内存模型的概念性规范;指令重排是导致没有可见性的原因之一.
(2)什么是可见性参加如下demo:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/** * 注意:该demo 展示了volatile变量在主内存和线程工作内存中的状态 * 1.一个线程获取回来的volatile变量会保持不变;除非重新获取一次 * 2.另外一个线程a改变了volatile变量的值,会立即同步到主内存; 当其他线程重新获取的时候,a对变量的改变可以被看到,这就是可见性. * */ public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //主线程获取test并打印值 Test test = Test.getTest(); Log.e("TAG", test.toString()); // 2.MyThread1获取test,sleep 2000ms后打印值 MyThread1 t1 = new MyThread1(); t1.start(); // 2.MyThread 重置Test,并打印值 MyThread t = new MyThread(); t.start(); } }); } static class MyThread extends Thread { @Override public void run() { Test test = Test.resetTest(); System.out.println("重置volatile变量为null"); System.out.println(test); } } static class MyThread1 extends Thread { @Override public void run() { Test test = Test.getTest(); System.out.println("刚获取后 打印volatile变量"); System.out.println(test); try { Thread.sleep(2000); System.out.println("sleep 了2000ms后 打印volatile变量"); System.out.println(test); System.out.println("sleep 了2000ms后 打印Test中的volatile变量"); System.out.println(Test.test); System.out.println("sleep 了2000ms后 再次获取test后打印volatile变量"); System.out.println(Test.getTest()); } catch (InterruptedException e) { e.printStackTrace(); } } } static class Test { private int mT; private static volatile Test test; private Test() { } public static Test getTest() { if (test == null) { test = new Test(); } return test; } public static Test resetTest() { test = null; return test; } } }

运行结果如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
2020-07-07 14:05:48.530 4772-4772/com.example.myapplication E/: com.example.myapplication.MainActivity$Test@d28ed69 2020-07-07 14:05:48.531 4772-4945/com.example.myapplication I/System.out: 刚获取后 打印volatile变量 2020-07-07 14:05:48.532 4772-4945/com.example.myapplication I/System.out: com.example.myapplication.MainActivity$Test@d28ed69 2020-07-07 14:05:48.532 4772-4946/com.example.myapplication I/System.out: 重置volatile变量为null 2020-07-07 14:05:48.532 4772-4946/com.example.myapplication I/System.out: null 2020-07-07 14:05:50.532 4772-4945/com.example.myapplication I/System.out: sleep 了2000ms后 打印volatile变量 2020-07-07 14:05:50.533 4772-4945/com.example.myapplication I/System.out: com.example.myapplication.MainActivity$Test@d28ed69 2020-07-07 14:05:50.533 4772-4945/com.example.myapplication I/System.out: sleep 了2000ms后 打印Test中的volatile变量 2020-07-07 14:05:50.533 4772-4945/com.example.myapplication I/System.out: null 2020-07-07 14:05:50.533 4772-4945/com.example.myapplication I/System.out: sleep 了2000ms后 再次获取test后打印volatile变量 2020-07-07 14:05:50.533 4772-4945/com.example.myapplication I/System.out: com.example.myapplication.MainActivity$Test@6e8a08f

参考:
1.http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf
2.http://ifeve.com/wp-content/uploads/2014/03/JSR133%E4%B8%AD%E6%96%87%E7%89%881.pdf
3.https://jcp.org/en/jsr/overview
4.JCP
https://zh.wikipedia.org/wiki/JCP

2.禁止 instance = new Singleton();的指令重排
注意:
instance = new Singleton()
分解为:
1.分配对象内存空间
2.初始化对象
3.将instance指向分配的内存空间
若不加 volatile;2和3两个步骤可以指令重排;这样就会造成Instance指向了内存空间,但实际未完成对象初始化.

这样会导致a线程只执行到:Instance指向了内存空间,但实际未完成对象初始化;
的时候b线程判断instance != null 而返回一个未完成初始化化的对象.

参考:
1.Java单例模式双重检查锁定中volatile关键字的作用
https://blog.csdn.net/zcl_love_wx/article/details/80758162

2.单例模式双重检查加锁为什么需要加上volatile关键字?
https://blog.csdn.net/fd2025/article/details/103837840

最后

以上就是等待面包最近收集整理的关于Java中写双重检查加锁的单例时volatile关键字作用的全部内容,更多相关Java中写双重检查加锁内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(68)

评论列表共有 0 条评论

立即
投稿
返回
顶部