共计 2473 个字符,预计需要花费 7 分钟才能阅读完成。
谈谈你对 volatile
的了解?
在 Java 中,volatile
是一个关键字,用于润饰变量。它的次要目标是保障多线程环境下的可见性和有序性。
当一个变量被申明为 volatile
时,它将具备以下个性:
- 可见性(Visibility):
volatile
变量在多线程环境下,当一个线程批改了该变量的值,其余线程能够立刻看到最新的值。这是因为volatile
变量的值会被立刻刷新到主内存中,并且读取操作时会从主内存中获取最新的值。 - 有序性(Ordering):
volatile
变量的读写操作都是依照申明程序执行的,不会被重排序。这意味着在多线程环境下,对volatile
变量的操作不会受到指令重排的影响,保障了操作的有序性。
然而,volatile
并不能解决所有的并发问题。它实用于以下场景:
- 状态标记:当一个变量用于示意状态标记,多个线程须要共享该标记并对其进行批改时,应用
volatile
能够确保所有线程都能看到最新的标记值。 - 双重查看锁定(Double-Checked Locking):在单例模式中,应用双重查看锁定能够在保障线程平安的状况下进步性能。在这种状况下,将单例对象申明为
volatile
能够防止指令重排导致的线程平安问题。
须要留神的是,volatile
并不能保障原子性。对于复合操作,例如 i++
,volatile
不能保障线程平安,须要应用其余的同步机制,例如 synchronized
或 Lock
。
总之,volatile
是一种轻量级的同步机制,用于保障变量的可见性和有序性。它实用于一些特定的并发场景,但并不能解决所有的并发问题。在编写多线程代码时,须要依据具体的需要抉择适合的同步机制。
volatile
关键字次要用于以下几个方面:
-
状态标记位:
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
的值后,其余线程能够立刻看到最新的值,从而实现线程间的通信。 -
双重查看锁定:
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
并不适用于所有的场景,有些状况下须要应用其余的同步机制,例如 synchronized
或 Lock
。
另外,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
,这是为了避免多个线程同时通过第一次查看,防止创立多个实例。如果 instance
为 null
,则创立一个新的实例。
通过双重查看锁定,只有在第一次创立实例时才会进行同步,后续获取实例的操作不须要进入同步块,进步了性能。同时,应用 volatile
关键字保障了实例的可见性,确保其余线程能够立刻看到最新的实例。
须要留神的是,双重查看锁定在晚期的 Java 版本中可能存在问题,因为 Java 的指令重排序可能导致一个线程在第一次查看时看到 instance
不为 null
,但实际上还没有实现实例化。在 Java 5 及以上版本,通过将 instance
申明为 volatile
能够解决这个问题。