什么是内存可见性
一个线程对主内存中的数据修改了,但是其他线程不可见。造成了内存可见性问题。
主内存是什么
工作内存是什么
存储私有变量
// 永不终止的循环 - 可见性问题
public class TestInfinityLoop {
static boolean stop = false; //停止标记
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
stop = true; // volatile 的写
System.out.println("t线程内存:" + stop);
});
System.out.println("start " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
System.out.println("主线程内存:" + stop);
t.start();
//Thread t2 = new Thread(() -> {
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// stop = true; // volatile 的写
// System.out.println("t2线程内存:" + stop);
//});
//t2.start();
//System.out.println("主线程内存:" + stop);
foo();
}
private static void foo() {
while (true) {
boolean b = stop; // volatile 的读
if (b) {
break;
}
}
System.out.println("end " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
}
}
start 17:30:32 主线程内存:false t线程内存:true
这个代码和视频里讲的,启动两个线程,其中stop会将主内存中值修改为TRUE,实际上,一个线程的线程结果都是将主内存中的值改为TRUE。和多线程无关。
我们来看看两个线程的结果打印
// 永不终止的循环 - 可见性问题
public class TestInfinityLoop {
static boolean stop = false; //停止标记
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
stop = true; // volatile 的写
System.out.println("t线程内存:" + stop);
});
System.out.println("start " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
System.out.println("主线程内存:" + stop);
t.start();
Thread t2 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
stop = true; // volatile 的写
System.out.println("t2线程内存:" + stop);
});
t2.start();
System.out.println("主线程内存:" + stop);
foo();
}
private static void foo() {
while (true) {
boolean b = stop; // volatile 的读
if (b) {
break;
}
}
System.out.println("end " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
}
}
最终输出都是
start 17:30:12 主线程内存:false 主线程内存:false t2线程内存:true t线程内存:true
初始状态, t 线程刚开始从主内存读取了 run 的值到工作内存
再尝试用 -Xint 强制解释执行
禁用JIT编译器优化命令
上面的结果start 18:10:44
主线程内存:false
t线程内存:true
end 18:10:45
再用 -XX:+PrintCompilation 看看实时编译后的代码
这个就代表着编译被优化了。解决方案
加上volatile(易变)的关键字
public class TestInfinityLoop {
volatile static boolean stop = false; //停止标记
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
stop = true; // volatile 的写
System.out.println("t线程内存:" + stop);
});
System.out.println("start " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
System.out.println("主线程内存:" + stop);
t.start();
foo();
}
private static void foo() {
while (true) {
boolean b = stop; // volatile 的读
if (b) {
break;
}
}
System.out.println("end " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
}
}
或者用synchronized解决
public class TestInfinityLoop {
static boolean stop = false; //停止标记
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
stop = true; // volatile 的写
}
System.out.println("t线程内存:" + stop);
});
System.out.println("start " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
System.out.println("主线程内存:" + stop);
t.start();
foo();
}
private static void foo() {
while (true) {
synchronized (lock) {
boolean b = stop; // volatile 的读
if (b) {
break;
}
}
}
System.out.println("end " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
}
}
总结
synchronized可以解决可见性、原子性、有序性,但是它是重型锁。而volatile能解决可见性和有序性,但不能解决原子性。