MST

星途 面试题库

面试题:Java内存模型之可见性问题

在Java内存模型中,解释什么是可见性问题,并举例说明如何通过关键字来解决可见性问题。
33.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

可见性问题

在Java内存模型中,可见性问题指的是当一个线程修改了共享变量的值,其他线程却不一定能立即看到这个修改。这是因为每个线程都有自己的工作内存,线程对共享变量的操作是在自己的工作内存中进行的,而不是直接在主内存中操作。只有当线程将工作内存中的数据刷新回主内存时,其他线程才能看到最新的值。如果线程之间没有正确的同步机制,就可能出现可见性问题,导致程序出现难以调试的错误。

解决可见性问题的关键字及示例

  1. 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变量没有被声明为volatilethread2可能永远无法看到thread1flag的修改,从而导致程序陷入死循环。通过将flag声明为volatile,可以确保thread2能够及时看到thread1flag的修改,从而正常结束循环并输出信息。

  1. 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();
    }
}

在这个示例中,setFlagcheckFlag方法都被synchronized修饰。当thread1调用setFlag方法修改flag的值并退出该方法时,flag的值会被刷新回主内存。当thread2调用checkFlag方法进入该方法时,会从主内存中读取flag的最新值,从而解决了可见性问题。不过与volatile相比,synchronized开销较大,因为它不仅保证可见性,还保证了原子性和互斥性。