【Java】请谈谈volatile有什么特点?为什么它能保证变量对所有线程的可见性他的特性有哪些?

前言

volatile是java内置的关键字,是java虚拟机提供的轻量级同步机制

  • 保证可见性
  • 不保证原子性
  • 禁止指令重排

一、volatile的作用是什么?

volatile是一个轻量级的synchronized,一般作用与变量,在多处理器开发的过程中保证了内存的可见性。相比于synchronized关键字,volatile关键字的执行成本更低,效率更高。

二、volatile的特性有哪些?

并发编程的三大特性为可见性、有序性和原子性。通常来讲volatile可以保证可见性和有序性。

  • 可见性:volatile可以保证不同线程对共享变量进行操作时的可见性。即当一个线程修改了共享变量时,另一个线程可以读取到共享变量被修改后的值。
  • 有序性:volatile会通过禁止指令重排序进而保证有序性。
  • 原子性:对于单个的volatile修饰的变量的读写是可以保证原子性的,但对于i++这种复合操作并不能保证原子性。这句话的意思基本上就是说volatile不具备原子性了。

三、为什么它能保证变量对所有线程的可见性?

在计算机程序中,当多个线程同时访问同一个变量时,可能会发生线程安全问题,其中之一是变量的可见性问题。这意味着一个线程在修改了一个变量的值之后,其他线程无法立即感知到这个变化,导致程序出现不一致的行为。

为了解决这个问题,Java提供了一个关键字volatile。使用volatile关键字声明的变量,其特点如下:

  1. 可见性:对于一个volatile变量的写操作,JVM会立即把修改后的值刷新回主内存中,而不是仅仅保留在本地缓存中。这样,其他线程就能够看到该变量的最新值,从而避免了可见性问题。

  2. 禁止指令重排:volatile关键字还可以禁止JVM对指令的重排优化。在不加volatile关键字的情况下,JVM为了提高性能,可能会对指令进行重排,导致程序出现意外的行为。而使用volatile关键字声明的变量,JVM会保证指令的执行顺序和程序的代码顺序一致,从而避免了这种问题。

接下来,我们通过一段代码来演示下volatile关键字如何保证可见性:

public class VolatileDemo {
    
    
    private volatile boolean flag = false;

    public void setFlag() {
    
    
        flag = true;
    }

    public void printFlag() {
    
    
        System.out.println("flag = " + flag);
    }

    public static void main(String[] args) {
    
    
        final VolatileDemo demo = new VolatileDemo();

        new Thread(() -> {
    
    
            try {
    
    
                Thread.sleep(1000); // 模拟线程1执行时间
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            demo.setFlag();
        }).start();

        new Thread(() -> {
    
    
            while (!demo.flag) {
    
     // 如果flag不是volatile,该循环可能会一直执行下去
                // do nothing
            }
            demo.printFlag();
        }).start();
    }
}

在这个代码中,我们声明了一个名为flag的布尔型volatile变量。在程序启动后,我们启动了两个线程,线程1会在1秒后将flag的值设为true,线程2会在flag变为true之前一直循环等待。由于flag是volatile类型,线程2能够正确感知到flag的变化,从而在flag变为true后打印出相应的信息。如果flag不是volatile类型,线程2可能会一直等待下去,因为它无法感知到flag的变化,从而导致程序出现错误的结果。

需要注意的是,volatile关键字虽然可以保证可见性和禁止指令重排,但并不能保证原子性。也就是说,如果一个变量被多个线程同时访问,并且这些线程都对它进行修改操作,那么使用volatile关键字并不能保证程序的正确性。这时候需要使用其他的线程同步机制,如synchronized关键字或者Lock接口等。

猜你喜欢

转载自blog.csdn.net/u011397981/article/details/132753955