面试题答案
一键面试可见性问题
在Java内存模型中,可见性问题指的是当一个线程修改了共享变量的值,其他线程却不一定能立即看到这个修改。这是因为每个线程都有自己的工作内存,线程对共享变量的操作是在自己的工作内存中进行的,而不是直接在主内存中操作。只有当线程将工作内存中的数据刷新回主内存时,其他线程才能看到最新的值。如果线程之间没有正确的同步机制,就可能出现可见性问题,导致程序出现难以调试的错误。
解决可见性问题的关键字及示例
volatile
关键字:volatile
关键字可以保证变量的可见性。当一个变量被声明为volatile
时,线程对该变量的修改会立即刷新到主内存,并且其他线程在读取该变量时会直接从主内存中读取,而不是从自己的工作内存中读取旧值。
示例代码如下:
public class VisibilityExample {
private volatile boolean flag = false;
public void setFlag() {
flag = true;
}
public void checkFlag() {
while (!flag) {
// 等待flag变为true
}
System.out.println("Flag is true now.");
}
public static void main(String[] args) {
VisibilityExample example = new VisibilityExample();
Thread thread1 = new Thread(() -> example.setFlag());
Thread thread2 = new Thread(() -> example.checkFlag());
thread2.start();
thread1.start();
}
}
在上述代码中,如果flag
变量没有被声明为volatile
,thread2
可能永远无法看到thread1
对flag
的修改,从而导致程序陷入死循环。通过将flag
声明为volatile
,可以确保thread2
能够及时看到thread1
对flag
的修改,从而正常结束循环并输出信息。
synchronized
关键字:虽然volatile
主要用于解决可见性问题,synchronized
关键字也可以间接解决可见性问题。当一个线程进入synchronized
块时,它会从主内存中读取共享变量的值,而当线程退出synchronized
块时,会将共享变量的值刷新回主内存。
示例代码如下:
public class SynchronizedVisibilityExample {
private boolean flag = false;
public synchronized void setFlag() {
flag = true;
}
public synchronized void checkFlag() {
while (!flag) {
// 等待flag变为true
}
System.out.println("Flag is true now.");
}
public static void main(String[] args) {
SynchronizedVisibilityExample example = new SynchronizedVisibilityExample();
Thread thread1 = new Thread(() -> example.setFlag());
Thread thread2 = new Thread(() -> example.checkFlag());
thread2.start();
thread1.start();
}
}
在这个示例中,setFlag
和checkFlag
方法都被synchronized
修饰。当thread1
调用setFlag
方法修改flag
的值并退出该方法时,flag
的值会被刷新回主内存。当thread2
调用checkFlag
方法进入该方法时,会从主内存中读取flag
的最新值,从而解决了可见性问题。不过与volatile
相比,synchronized
开销较大,因为它不仅保证可见性,还保证了原子性和互斥性。