关于java:volatile关键字

1次阅读

共计 2473 个字符,预计需要花费 7 分钟才能阅读完成。

谈谈你对 volatile 的了解?


在 Java 中,volatile 是一个关键字,用于润饰变量。它的次要目标是保障多线程环境下的可见性和有序性。

当一个变量被申明为 volatile 时,它将具备以下个性:

  1. 可见性(Visibility):volatile 变量在多线程环境下,当一个线程批改了该变量的值,其余线程能够立刻看到最新的值。这是因为 volatile 变量的值会被立刻刷新到主内存中,并且读取操作时会从主内存中获取最新的值。
  2. 有序性(Ordering):volatile 变量的读写操作都是依照申明程序执行的,不会被重排序。这意味着在多线程环境下,对 volatile 变量的操作不会受到指令重排的影响,保障了操作的有序性。

然而,volatile 并不能解决所有的并发问题。它实用于以下场景:

  1. 状态标记:当一个变量用于示意状态标记,多个线程须要共享该标记并对其进行批改时,应用 volatile 能够确保所有线程都能看到最新的标记值。
  2. 双重查看锁定(Double-Checked Locking):在单例模式中,应用双重查看锁定能够在保障线程平安的状况下进步性能。在这种状况下,将单例对象申明为 volatile 能够防止指令重排导致的线程平安问题。

须要留神的是,volatile 并不能保障原子性。对于复合操作,例如 i++volatile 不能保障线程平安,须要应用其余的同步机制,例如 synchronizedLock

总之,volatile 是一种轻量级的同步机制,用于保障变量的可见性和有序性。它实用于一些特定的并发场景,但并不能解决所有的并发问题。在编写多线程代码时,须要依据具体的需要抉择适合的同步机制。



volatile 关键字次要用于以下几个方面:

  1. 状态标记位:

    public class Example {
     private volatile boolean flag = false;
    
     public void setFlag() {flag = true;}
    
     public void doSomething() {while (!flag) {// 期待标记位变为 true}
         // 执行工作
     }
    }

    在上述示例中,flag 被申明为 volatile,它用作一个状态标记位。doSomething() 办法在一个循环中期待 flag 变为 true,而 setFlag() 办法用于批改 flag 的值。应用 volatile 润饰 flag 能够保障在一个线程批改了 flag 的值后,其余线程能够立刻看到最新的值,从而实现线程间的通信。

  2. 双重查看锁定:

    public class Singleton {
     private volatile static Singleton instance;
    
     private Singleton() {// 公有构造函数}
    
     public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();
                 }
             }
         }
         return instance;
     }
    }

    在上述示例中,instance 被申明为 volatile,用于保障多线程环境下的可见性。双重查看锁定是一种提早加载的单例模式实现形式,应用 volatile 能够防止指令重排导致的线程平安问题。

须要留神的是,volatile 并不适用于所有的场景,有些状况下须要应用其余的同步机制,例如 synchronizedLock

另外,volatile 还能够用于润饰数组类型的变量、援用类型的变量等,用法相似于上述示例。但须要留神的是,volatile 并不能保障数组元素或援用对象的可见性,只能保障数组变量或援用变量自身的可见性。在须要保障数组元素或援用对象的可见性时,须要应用其余的同步机制。



什么是双重查看锁定?

双重查看锁定(Double-Checked Locking)是一种提早加载的单例模式实现形式,旨在在保障线程平安的同时进步性能。

在传统的单例模式实现中,通常应用懒汉式(Lazy initialization)或饿汉式(Eager initialization)来创立单例对象。然而,这些实现形式在多线程环境下可能存在线程平安问题或性能问题。

双重查看锁定通过应用 volatile 关键字和同步块来解决这些问题。其根本思维是,在获取锁之前先查看一次实例是否曾经被创立,如果没有被创立,则进入同步块创立实例。

以下是双重查看锁定的示例代码:

public class Singleton {
    private volatile static Singleton instance;

    private Singleton() {// 公有构造函数}

    public static Singleton getInstance() {if (instance == null) { // 第一次查看,防止不必要的同步
            synchronized (Singleton.class) {if (instance == null) { // 第二次查看,确保只有一个线程创立实例
                    instance = new Singleton();}
            }
        }
        return instance;
    }
}

在上述代码中,instance 被申明为 volatile,用于保障多线程环境下的可见性。首先,查看 instance 是否为 null,如果为 null,则进入同步块。在同步块中再次查看 instance 是否为 null,这是为了避免多个线程同时通过第一次查看,防止创立多个实例。如果 instancenull,则创立一个新的实例。

通过双重查看锁定,只有在第一次创立实例时才会进行同步,后续获取实例的操作不须要进入同步块,进步了性能。同时,应用 volatile 关键字保障了实例的可见性,确保其余线程能够立刻看到最新的实例。

须要留神的是,双重查看锁定在晚期的 Java 版本中可能存在问题,因为 Java 的指令重排序可能导致一个线程在第一次查看时看到 instance 不为 null,但实际上还没有实现实例化。在 Java 5 及以上版本,通过将 instance 申明为 volatile 能够解决这个问题。

正文完
 0