问题描述:
博主在学习volatile时,看到java语言层面支持的可见性实现方式有以下两种:
1>synchronized
2>volatile
对于synchronized实现可见性的解释是:synchronized会清空线程的工作内存,并从主内存中重新获取变量的值。相信许多人和我一样,看到这个解释的时候都是一脸懵逼的,下面我将通过一个案例说明synchronized如何实现可见性。
多线程下共享变量不可见问题描述(知道的同学可以跳过):
首先需要2个线程,一个主线程,一个自定义线程
自定义线程代码如下:
class MyThread01 extends Thread{
private boolean flag01 = true;
@Override
public void run(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag01 = false;
}
public boolean getFlag01(){
return flag01;
}
}
自定义线程中有个成员变量flag01,初始默认值为true。自定义线程的run方法睡眠5秒后将flag01的值修改为false。
public static void main(String[] args){
MyThread01 myThread01 = new MyThread01();
myThread01.start();
while(true){
if (myThread01.getFlag01()){
System.out.println(111);
}
}
}
主线程先启动自定义线程,然后在死循环中判断flag01的值,若为true则输出111,若为false则不处理。
以上代码运行的结果为:主线程无限输出111,自定义线程5秒后修改flag01的值为flase后依然会继续输出111。
自定义线程修改完共享变量flag01后,主线程不知道,这就是多线程下共享变量不可见问题。这是由于JMM中,每个线程都有自己的工作内存,在从主内存中获取到变量的值后,即使主内存中该变量发生改变,任然会使用工作内存中的值来操作。
synchronized实现可见性:
一定要在获取共享变量之前执行synchronized语句块,synchronized()的括号中随便放入什么,因为实现可见性与锁的机制无关。自定义线程的成员变量flag01默认值为false,run方法睡眠5秒后将flag01的值改为true,再睡眠5秒后将flag01的值改为false。
public class Test01{
public static void main(String[] args){
MyThread01 myThread01 = new MyThread01();
myThread01.start();
while(true){
synchronized (myThread01){
if (myThread01.getFlag01()){
System.out.println(111);
}
}
}
}
}
class MyThread01 extends Thread{
private boolean flag01 = false;
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag01 = true;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag01 = false;
}
public boolean getFlag01(){
return flag01;
}
}
main方法执行结果为:首先什么也不输出(flag01默认为false),5秒后输出111(5秒后run方法修改flag01的值为true),再5秒后什么也不输出(再睡眠5秒后将flag01的值改为false)。
由此可见synchronized实现可见性准确的解释是:synchronized清空了线程的工作内存,当执行到获取共享变量的代码时(getFlag01()方法),会重新从主内存中获取,使得线程工作内存与主内存的值一致。
总结:
synchronized实现可见性与锁的机制无关,而是由于synchronized会清空线程的工作内存,每次执行到获取共享变量的代码时(执行getFlag01()方法时)都会重新从主内存中获取共享变量,因此获得的是共享变量最新值。