1.保证多进程情况下变量的可见性
(1)可见性是java内存模型的概念性规范;指令重排是导致没有可见性的原因之一.
(2)什么是可见性参加如下demo:
/**
* 注意:该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;
}
}
}
运行结果如下:
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