一、原理
- 底层实现是内存屏障,Memory Barrier(Memory Fence)
1. 可见性
1.1 写屏障(sfence)
- 被voliate修饰的变量,会在写操作后,加上写屏障
- 在该屏障之前的所有改动,都会同步到主存中去
package com.dreamer.multithread.day02;
public class Demo03 {
private static int number = 0;
private static volatile int age = 0;
public static void main(String[] args) {
new Thread(() -> {
number++;
age++;
// JVM 会加上写屏障
}).start();
}
}
1.2 读屏障(sfence)
package com.dreamer.multithread.day02;
public class Demo03 {
private static int number = 0;
private static volatile int age = 0;
public static void main(String[] args) {
new Thread(() -> {
// JVM 会加上读屏障
int numberResult = number;
int ageResult = age;
}).start();
}
}
2. 有序性
2.1 写屏障(sfence)
- 写屏障会将保证屏障上面的代码不会重排,也就是说不会将屏障上面的代码挪到屏障下面执行
2.1 读屏障(sfence)
3. 不能保证指令交错
- volatile不能保证不同线程在执行的时候的指令交错引发的问题
- 因为它只是保证各个线程在执行的时候都从主存中去加载
- 保证指令不会发生交错: 用synchronized来进行枷锁
二、happen-before原则
- 规定了对共享变量的写操作,对其他线程的读操作的可见性总结
- 是可见性和有序性的一套总结
1. synchronized
- 线程解锁m之前对变量的写,对于接下来的用m加锁的其他线程的读,改变是可见的
package com.dreamer.multithread.day02;
public class Demo04 {
private static int x = 0;
private static Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock) {
x = 10;
}
}).start();
new Thread(() -> {
synchronized (lock) {
System.out.println(x);
}
}).start();
}
}
2. volatile
- 变量用volatile修饰,一个线程对其的修改,对于其他线程来说是可见的
package com.dreamer.multithread.day02;
public class Demo05 {
private static volatile int x = 0;
public static void main(String[] args) {
new Thread(() -> x = 10).start();
new Thread(() -> System.out.println(x)).start();
}
}
3. 起步
- 线程start前对变量的写操作,对该线程开始后的读操作是可见的
package com.dreamer.multithread.day02;
public class Demo06 {
private static int x = 0;
public static void main(String[] args) {
x = 10;
new Thread(() -> System.out.println(x)).start();
}
}
4
- 线程结束前对变量的写操作,对其他线程得知它结束后的读操作可见性(如调用join方法)
package com.dreamer.multithread.day02;
public class Demo07 {
private static int x = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> x = 10);
thread.start();
thread.join();
System.out.println(x);
}
}
5. 默认值
- 对变量默认值的写,0,false,null,其他线程对该变量可见
6. 传递性
package com.dreamer.multithread.day02;
public class Demo08 {
private static int x = 0;
private static volatile int y = 0;
public static void main(String[] args) {
new Thread(() -> {
x = 10;
y = 20;
// 写屏障,会将上面的操作全部赋值到主存中去
}).start();
new Thread(() -> {
System.out.println(x);
System.out.println(y);
}).start();
}
}