Java 中的 volatile 关键字
-
Java 中的
volatile
关键字 -
在 Java 中我们什么时候使用
volatile
关键字 -
Java 中
volatile
关键字的性能优势 -
在 Java 中使用
volatile
关键字 - 结论
Java 是一种非常流行的编程语言,通过了解 Java,我们可以很容易地理解它为什么会在编程社区中获得这样的地位。
Java 为我们提供了大量有用的资源,我们可以在程序中使用这些资源来编写所需的逻辑。
Java 对于多用途编程问题很有用,其中一个这样的编程问题是使用线程或不同逻辑程序的并行执行。
本文将探索和理解 Java 中的 volatile
关键字、它的属性以及它在实际编程中的用法。我们将了解何时使用 volatile
关键字、其重要性以及何时以及不应使用。
本文还将提供一个基本的示例代码来帮助你理解 volatile
关键字。
Java 中的 volatile
关键字
在使用 Java 时,你可能遇到过并行线程和 synchronized
关键字。volatile
关键字与 synchronized
关键字相似,但又完全不同。
volatile
关键字在我们的代码中很容易理解和实现,并且比 synchronized
锁定机制具有更少的运行时开销。然而,volatile
关键字不如 synchronized
锁定强大。
volatile
关键字确保对共享 volatile
变量所做的更改立即反映在所有线程中。synchronized
关键字用于实现 Java 中的锁机制,锁提供可见性和原子性。
但是,volatile
关键字只能实现可见性,不能实现原子性。因此,我们可以理解,volatile
关键字只能在极少数情况下使用。
例如,当变量的值独立于所有其他变量和案例,甚至独立于自身时,我们可以使用它。
在 Java 中我们什么时候使用 volatile
关键字
正如我们已经看到的,在 volatile
关键字的帮助下,共享变量的更改值会立即反映在所有其他线程中。因此,我们在处理线程中的共享变量时使用它。
当 synchronized
关键字已经可用时,你可能会问为什么我们使用 volatile
关键字?但是,volatile
关键字更易于理解且性能更好。
因此,在某些情况下,我们可能想要使用它。例如,当我们有大量的读取操作和少量的写入操作时,我们希望使用 volatile
关键字以获得更好的性能。
此外,volatile
关键字提供了更好的可伸缩性,因为它不会导致线程阻塞。
为了更好地理解我们可以安全有效地使用 volatile
关键字的情况,我们应该注意以下情况。
- 程序的当前和其他变量和状态之间不应存在相互依赖关系。
- 变量不应依赖于自身。
因此,如果我们的变量完全独立于自身,我们可以使用代码的 volatile
关键字。
但是,我们经常遇到不满足第一个或第二个条件或两个条件的情况。因此,volatile
关键字的使用受到很大限制。
请注意许多程序员犯的一个非常微妙的错误。很多时候,程序员倾向于将增量运算符视为单个操作。
但是,增量运算符(或减量运算符)由多个操作组成,并且发生在多个阶段。因此,在这种情况下,我们不应该使用 volatile
关键字。
Java 中 volatile
关键字的性能优势
虽然 volatile
关键字简单易用,但它的性能优于锁定机制。在某些情况下,可以看出 volatile
的性能比 synchronized
好得多。
然而,事情并没有那么简单。当我们有 Java 虚拟机(JVM)时,很难正确判断这两个关键字的性能。
是因为 JVM 进行了优化,有时候加锁是不需要的,JVM 在代码优化的时候把它去掉了。但是,通常观察到易失性
读取在性能方面非常好,并且可以与普通读取相媲美。
然而,易失性
写入比普通写入要昂贵得多。另一方面,易失性
写入总是比获取锁便宜。
因此,我们应该考虑 volatile
关键字和 synchronized
锁定机制之间的性能比较。
我们已经讨论了 volatile
的另一个性能优势:它不会导致线程阻塞并且在可扩展性方面要好得多。这是使用 volatile
关键字的另一个原因。
在 Java 中使用 volatile
关键字
我们将在下面的代码中了解如何在我们的 Java 代码中使用 volatile
关键字。
sharedObj.java
public class sharedObj {
volatile boolean flag;
}
ThClass.java
public class ThClass extends Thread {
public sharedObj obj;
public ThClass(sharedObj obj) {
this.obj = obj;
}
@Override
public void run() {
super.run();
while (true) {
if (this.obj.flag)
this.obj.flag = false;
else
this.obj.flag = true;
System.out.println(obj.flag);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
主类.java
import java.util.*;
public class MainClass {
public static void main(String[] args) {
sharedObj obj = new sharedObj();
ThClass th1, th2;
th1 = new ThClass(obj);
th2 = new ThClass(obj);
th1.run();
th2.run();
}
}
在上面给出的 Java 程序中,我们创建了三个不同的类。这些类解释如下。
sharedObj.java
:我们已经声明了一个在这个类中的线程之间共享的可变共享变量。ThClass.java
:这个类用于创建线程。它扩展了Thread
类并覆盖了run()
方法。每个线程休眠 1 秒。MainClass.java
:Main
类是实现main()
方法的最重要的类,从执行开始。我们创建了两个线程,并通过构造函数传递了一个共享对象作为参数。
该 Java 程序的输出如下所示。
true
false
true
false
true
false
请注意,此代码运行无限时间,要停止它,你必须手动停止它的执行。 (例如,在 Linux 上按 CTRL+C)。
结论
在本文中,我们了解了 volatile
关键字、它的优缺点以及何时可以使用它。虽然 volatile
关键字不如同步锁定机制强大,但它仍然在实践中使用。
我们仍然使用 volatile
关键字,因为它在代码中更容易理解和实现,比锁具有更好的性能,并且更容易扩展,因为它不锁定线程。
但是,在代码中使用 volatile
关键字时,应始终小心检查条件,以免发生意外错误。
本文提到了检查 volatile
关键字是否适用的简单步骤;检查使用这些步骤以了解 volatile
是否适用于你的代码。
这将有助于在同步锁定机制和 volatile
关键字之间进行选择。