首页常见问题正文

实现可见性的方法有哪些?

更新时间:2023-07-25 来源:黑马程序员 浏览量:

IT培训班

  在Java中,实现可见性(visibility)的主要方法是使用关键字volatile和使用锁(如synchronized关键字或 java.util.concurrent包中的锁)来确保对共享变量的修改在多线程环境中能够正确地被其他线程所观察到。

  下面详细说明这两种方法,并附带代码演示。

  1.使用volatile关键字:

  volatile是一种轻量级的同步机制,用于告诉JVM对被修饰的变量不进行缓存,直接从主内存中读取和写入数据。这样可以确保当一个线程修改了变量的值时,其他线程能够立即看到这个变化,而不是使用缓存中的旧值。

public class VolatileVisibilityExample {
    private volatile boolean flag = false;

    public void setFlag(boolean value) {
        flag = value;
    }

    public void checkFlag() {
        while (!flag) {
            // Busy waiting until the flag becomes true
        }
        System.out.println("Flag is now true!");
    }

    public static void main(String[] args) {
        VolatileVisibilityExample example = new VolatileVisibilityExample();

        new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            example.setFlag(true);
        }).start();

        new Thread(() -> {
            example.checkFlag();
        }).start();
    }
}

  在上面的例子中,我们创建了一个VolatileVisibilityExample类,其中的flag变量被声明为volatile。第一个线程在启动后会等待1秒钟然后将flag设置为true,而第二个线程在flag变为true之前会一直进行忙等待(busy waiting)。由于flag是volatile的,第二个线程能够看到第一个线程对flag的修改,并在flag变为true时结束忙等待。

  2.使用锁:

  另一种实现可见性的方法是使用锁,通过synchronized关键字或者java.util.concurrent包中的锁机制来保护对共享变量的访问。

public class LockVisibilityExample {
    private boolean flag = false;
    private final Object lock = new Object();

    public void setFlag(boolean value) {
        synchronized (lock) {
            flag = value;
        }
    }

    public void checkFlag() {
        synchronized (lock) {
            while (!flag) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("Flag is now true!");
    }

    public static void main(String[] args) {
        LockVisibilityExample example = new LockVisibilityExample();

        new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            example.setFlag(true);
            synchronized (example.lock) {
                example.lock.notifyAll();
            }
        }).start();

        new Thread(() -> {
            example.checkFlag();
        }).start();
    }
}

  在上面的例子中,我们创建了一个LockVisibilityExample类,其中使用了一个名为lock的对象作为锁。在setFlag和checkFlag方法中,我们使用synchronized关键字来保护对flag的访问。第一个线程设置flag为true并通过synchronized块的notifyAll()方法通知第二个线程,而第二个线程则在flag变为true之前一直等待,并在被通知后结束等待。

  这两种方法都可以实现可见性,但使用volatile更为简单和轻量级。然而,在某些情况下,使用锁可能会更有优势,例如需要进行更复杂的操作或需要更精细地控制对共享资源的访问。

分享到:
在线咨询 我要报名
和我们在线交谈!