一、为什么要有volatile关键字
估计很多java初学者都被volatile这个关键字迷惑过。虽然网上有很多讨论volatile的文章,但它们有的过于讲述底层原理,而没有说明其应用场景,让初学者看后还是一头雾水;有的过于使用类比讲解,造成了一定的错误,这样的文章更害人。下面,小弟试着分析下volatile关键字的作用及用法,希望能给大家带来一定的启发。文中错误之处,请各位大神指正。
我们知道,在多线程编程中,多个线程在访问共享变量时,必须进行必要的同步,否则很可能产生错误。synchronized关键字可以用来提供这种同步。该关键字主要将方法或者代码块设定为同步的方法或者同步的代码块。这种同步可以提供如下两个保证:
1、原子性。不管这个方法或者代码块被多少线程访问,方法和代码块中的语句只能作为一个整体执行,即当一个线程访问一个同步方法时,只有当方法执行完时,其他线程才能执行该方法。简单讲,就是一个线程一个线程来,而且必须全部执行完同步方法或代码块中的语句。
2、内存可见性。这指的是一个线程在同步方法或者代码块中对共享变量做的任何改变,对随后的其他线程都是可见的。
这种同步机制就像是一套严整的西装,很标准,但就是穿起来有点别扭,开销也大。有时用起来不是很方便,比如,在程序中有一个状态变量,被多个线程读取,我们只要求每个线程读取到的值都是最新值,并根据最新状态采取不同的操作。这种情况下,java为我们提供了一个轻量级的解决方案,就是使用volatile。
二、volatile关键字的作用是什么
volatile的英文意思是“易变的,不稳定的;(液体或油)易挥发的;爆炸性的;快活的,轻快的”。可见,单从字面上理解,volatile应该用在易变、不稳定的变量上。事实上,确实如此,这个关键字的作用就是告诉编译器,凡是被该关键字声明的变量都是易变的、不稳定的。所以不要试图对该变量使用缓存等优化机制,而应当每次都从它的内存地址中去读取值。使用volatile标记的变量在读取或写入时不需要使用锁,这将减少产生死锁的概率,使代码保持简洁。
请注意,这里只是说每次读取volatile的变量时都要从它的内存地址中读取,并没有说每次修改完volatile的变量后都要立刻将它的值写回内存。也就是说volatile只提供了内存可见性,而没有提供原子性。
三、什么时候使用volatile关键字
知道了volatile的作用,我们也就知道了它应该用在哪些地方。很显然,最好是那种只有一个线程修改变量,多个线程读取该变量的地方。也就是对内存可见性要求高,而对原子性要求低的地方。
举个例子,我们写游戏时,经常需要定义一个游戏的状态变量,并在主线程中根据不同条件修改它。为了提高游戏运行效率,我们通常并发用多个线程处理程序数据,这些线程都要访问游戏的状态变量,才能根据游戏状态进行相应的操作。这个时候用volatile就再合适不过了。