HappenBefore
指令重排:执行代码的顺序可能与编写的代码顺序不一致,即虚拟机优化代码,则为指令重排,目的是为了优化程序性能
happen-before:编译器或运行时环境为了优化程序性能
而采取的对指令重新排序执行的一种手段
例子:
package com.tsymq.thread.advanced;
public class HappenBefore {
private static int a = 0;
private static boolean flag = false;
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
a = 0;
flag = false;
// 线程1 读取数据
Thread t1 = new Thread( () -> {
a = 1;
flag = true;
});
// 线程2 更改数据
Thread t2 = new Thread( () -> {
if(flag){
a *= 1;
}
if(a == 0){
System.out.println("happen before: a = " + a);
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
可能出现控制台输出 happen before: a = 1 的情况
volatile
-
用于保证数据的同步,即可见性 (轻量级synchronized)
-
可以避免指令重排
-
会破坏原子性
public class VolatileTest {
private static int num = 0;
public static void main(String[] args) {
new Thread( () -> {
while(num == 0){
}
}).start();
// 1秒后改变num的值
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
}
}
代码运行,程序不会停止,因为while(num == 0)的死循环导致机器没有时间去将num的值同步更新
public class VolatileTest {
private volatile static int num = 0;
public static void main(String[] args) {
new Thread( () -> {
while(num == 0){
}
}).start();
// 1秒后改变num的值
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
}
}
在声明num变量时添加了volatile关键字,程序1秒钟后会停止运行
单例模式
套路:
- 构造器私有化,外部无法用new构造
- 提供私有的静态属性,存储对象的地址
- 提供公共的静态方法,获取属性
在并发控制下使用double-checking、volatile、synchronized实现单例模式
package com.tsymq.thread.advanced;
public class DoubleCheckedLocking {
private volatile static DoubleCheckedLocking instance; // 懒汉式
// 没有volatile,其他线程可能会拿到一个未初始化的空对象
private DoubleCheckedLocking(){
}
public static DoubleCheckedLocking getInstance(){
// 再次检测
if (null != instance){
return instance;
}
synchronized (DoubleCheckedLocking.class){
if(null == instance){
instance = new DoubleCheckedLocking();
}
}
return instance;
}
public static void main(String[] args) {
Thread t = new Thread( ()-> {
System.out.println("1" + DoubleCheckedLocking.getInstance());
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("2" + DoubleCheckedLocking.getInstance());
}
}