Java线程面试题(03) Java中的volatile如何工作? Java中的volatile关键字示例

本文为本博主翻译,未经允许,严禁转载!!!

简介

什么是Java中的volatile变量以及何时使用Java中的volatile变量是一道Java面试中经常被问及的多线程访问问题。尽管许多程序员知道什么是volatile变量,但是他们在回答第二部分问题什么地方使用volatile变量时失败,因为大部分人对Java中volatile变量并没有一个清晰的理解和应用。在本教程中,我们将通过提供一个Java中volatile变量的简单示例来解决这个问题,并讨论在Java中使用volatile变量的一些情况。无论如何,Java中的volatile关键字被用作Java编译器和Thread的指示器:它不会缓存这个变量的值,并且总是从主内存中读取它。所以如果你想共享任何变量,其中读写操作是通过实现原子的。比如读取和写入一个int或一个布尔变量,你可以声明他们作为volatile变量。

Java 5除了自动装箱,枚举,泛型和可变参数等主要变化外,Java引入了一些Java内存模型(JMM)的变化,它保证了一个线程的变好对另一个线程的可视性,也被解释为“发生之前”来解决内存的写入问题。在一个线程中发生的内存写入会“泄漏”,并被另一个线程看到。
Java volatile关键字不能与方法或类一起使用,只能与变量一起使用。 Java可变关键字还保证可见性和排序,Java 5之后, 写入任何volatile变量发生在任何读取到volatile变量之前。顺便说一句,使用volatile关键字还可以防止编译器或JVM对代码进行重新排序或将其从同步障碍中移走。

Java中的volatile变量示例

为了理解java中的volatile关键字的例子,我们回到Java中的Singleton模式。

/**
 * Java program to demonstrate where to use Volatile keyword in Java. 
 * In this example Singleton Instance is declared as volatile variable to ensure 
 * every thread see updated value for _instance. 
 * @author Javin Paul
 */
public class Singleton {
	private static volatile Singleton _instance;

	// volatile variable
	public static Singleton getInstance() {
		if (_instance == null) {
			synchronized (Singleton.class) {
				if (_instance == null)
					_instance = new Singleton();
			}
		}
		return _instance;
	}
}
如果仔细查看代码,您将能够弄清楚:
1)我们只是一次创建实例
2)在第一次请求来的时候创建实例。


如果我们不定义_instance为volatile变量,则创建Singleton的实例的Thread不能与其它线程通信,告知该实例已经被创建直到它退出Singleton同步块,所以如果线程A正在创建Singleton实例并且随后线程切换,所有其他线程将不能将_instance的值看作非null,并且它们将认为它仍然为空。

为什么?因为读线程没有进行任何锁定,直到写线程退出同步块,内存将不会同步,_instance的值不会在主内存中更新。在Java中使用Volatile关键字时,这是由Java自己处理的,所有读者线程都可以看到这些更新。

因此,除了Java中的同步关键字文章中相关总结外,volatile关键字还用于在线程之间传递内存内容。

我们来看看Java中volatile关键字的另一个例子

在写游戏的大部分时间里,我们使用一个变量bExit来检查用户是否按下了退出按钮,这个变量的值在事件线程中被更新,并在游戏线程中被检查过,所以如果我们没有在这个变量中使用volatile关键字,Game Thread可能会错过来自事件处理程序线程的更新,如果它在Java中不同步。 Java中的volatile关键字保证volatile变量的值总是从主内存中读取,而Java Memory模型中的“happen-before”关系将确保内存的内容将被传送到不同的线程。

private boolean bExit;

while(!bExit) {
   checkUserPosition();
   updateUserPosition();
}
在这个代码示例中,一个线程(游戏线程)可以缓存“bExit”的值,而不是每次从主内存获取它,如果在任何其他线程(事件处理线程)之间改变值;它不会被这个线程看到。在java中使布尔变量“bExit”变成volatile可以确保不会发生这种情况。
为了真正理解这个复杂的概念, 我还建议您阅读Brian Goetz撰写的Java Concurrency in Practice书中关于volatile变量的主题。

何时在Java中使用volatile变量

在volatile关键字的学习中最重要的一点是了解何时在Java中使用volatile变量。许多程序员知道什么是volatile变量,它是如何工作的,但他们从来没有真正使用volatile来实现任何实际目的。这里有几个示例演示何时在Java中使用Volatile关键字:
1)如果你想以原子方式读写long类型变量和double类型变量,可以使用Volatile变量。 long和double都是64位数据类型,默认情况下,long和double的写入不是原子和平台依赖。许多平台在长和双变量2步执行写操作,在每一步写32位,由于这可能使一个线程看到从两个不同的写32位。您可以通过在Java中使用long和double变量volatile来避免此问题。
2)在某些情况下,volatile可以用作实现Java同步的一种替代方法,如Visibility。用volatile变量,保证所有读写器线程在写操作完成后都会看到volatile变量的更新值,而不用volatile关键字,不同的读线程可能会看到不同的值。
3)volatile变量可用于通知编译器一个特定的字段被多个线程访问,这将阻止编译器进行任何重新排序或在多线程环境中不希望的任何类型的优化。如果没有volatile变量,编译器可以重新排序代码,可以自由地缓存volatile变量的值,而不是总是从主内存中读取数据。就像下面没有volatile变量的例子可能会导致无限循环

private boolean isActive = thread; 
public void printMessage(){ 
    while(isActive) { 
	    System.out.println("Thread is Active"); 
    } 
} 
如果没有volatile修饰符,则不能保证一个线程从其他线程看到isActive的更新值。编译器还可以自由缓存isActive的值,而不是在每次迭代中从主存储器中读取。通过使isActive成为一个volatile变量,可以避免这些问题。

4)另一个可以使用volatile变量的地方是在Singleton模式下修复双重检查的锁定。

Java中的Volatile关键字的重点

1)Java中的volatile关键字只适用于一个变量,使用volatile关键字和class和method是非法的。
2)Java中的volatile关键字保证volatile变量的值总是从主内存中读取,而不是从Thread的本地缓存中读取。
3)在Java中,对于使用Java volatile关键字(包括长变量和双变量)声明的所有变量,读写操作是原子操作。
4)在变量Java中使用volatile关键字可降低内存一致性错误的风险,因为在Java中对volatile变量的任何写入都会与随后的同一变量的读取之间建立一个happen-before关系。
5)从Java 5,volatile变量的更改对其他线程始终可见。更重要的是,这也意味着当一个线程在Java中读取一个volatile变量时,它不仅会看到volatile变量的最新变化,而且还会看到导致变化的代码的副作用。
6)即使没有在Java中使用volatile关键字,读取和写入也是原子参考变量是大多数原始变量(除了long和double之外的所有类型)。
7)对Java中的volatile变量的访问从来没有机会阻止,因为我们只是做一个简单的读或写操作,所以不像一个同步块,我们永远不会持有任何锁或等待任何锁。
8)作为对象引用的Java volatile变量可能为空。
9)Java volatile关键字并不意味着原子化,它在声明volatile之后会是原子的常见误解,为了使操作原子化,还需要使用Java中的同步方法或块来确保独占访问。
10).如果多个线程之间不共享变量,则不需要在该变量中使用volatile关键字。

Java中的synchronized和volatile关键字之间的区别

volatile和synchronized之间的区别, 是另一个流行的关于多线程和并发访问的核心Java面试问题。记住volatile不是synchronized关键字的替代品,但可以在某些情况下用作替代品。 这里列出了一些Java中volatile和synchronized关键字之间的差异:
1) volatile关键字是一个字段修饰符,synchronized修改代码块和方法。
2) synchronized获取并释放监视器的锁, volatile关键字不需要这个锁。
3)Java中的线程可以被阻塞,以便在同步的情况下等待任何监视器,这与Java中的volatile关键字不同。
4)在Java中,synchronized方法相比volatile关键字, 会影响性能
5) 由于Java中的volatile关键字只同步线程内存和“主”内存之间的一个变量的值,synchronized同步线程内存和“主”内存之间的所有变量的值,并锁定和释放监视器。由于这个原因,Java中的synchronized关键字可能比volatile更具开销。
6)您不能同步空对象,但Java中的volatile变量可能为空。
7) 从Java 5写入volatile字段与监视器释放具有相同的记忆效应,并且从易失性字段读取具有与监视器获取相同的记忆效应

简而言之,Java中的volatile关键字不是synchronized块或方法的替代,但在某些情况下非常方便,可以节省Java中使用同步带来的性能开销。如果你想了解更多关于易失性的内容,我也建议在Java内存模型的常见问题上进行解释。

原文链接

How Volatile in Java works? Example of volatile keyword in Java



  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值