在研究volatile关键字之前,首先看一段代码。
public class RunThread extends Thread{ private boolean isRunning =true; public boolean isRunning() { return isRunning; } public void setRunning(boolean isRunning) { this.isRunning = isRunning; } @Override public void run() { System.out.println("进入run方法了"); while(isRunning){ } System.out.println("线程被停止了"); } }
主线程
public class Run { public static void main(String[] args) { try { RunThread thread=new RunThread(); thread.start(); Thread.sleep(1000); thread.setRunning(false); System.out.println("已经赋值为false了"); } catch (Exception e) { e.printStackTrace(); } } }
控制台:
可以发现,线程并未停止。本人使用的运行环境的jdk1.8 64位,win10 64。
出现这个问题的原因是私有堆栈中的值和公共堆栈中的值不同步造成的。解决这样的问题就要使用volatile关键字。
volatile:当线程访问isRunning这个变量的时候,强制从公共堆栈中进行取值。现在将RunThread.java中的isRunning 使用volatile关键字修饰。
volatile private boolean isRunning =true;
运行,发现控制台输出如下:
通过使用volatile关键字,强制的从公共内存中读取变量的值,内存结构如下图:
使用volatile关键字增加实例变量在多个线程之间的可见性。但volatile关键字最致命的缺点是不支持原子性。
下面将关键字synchronized和volatile进行一下比较:
1)关键字volatile是线程同步的轻量级实现,所以volatile性能肯定不synchronized要好,并且volatile只能修饰变量,而synchronized可以修饰方法吗,一级代码块。
2)多线程访问volatile不会发生阻塞,而synchronized会出现阻塞。
3)volatile保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存中和公共内存中的数据做同步。
4)再次重申一下,volatile解决的是变量在多个线程之间的可见性:而synchronized解决的是多线程之间访问资源的同步性。
线程安全包含原子性和可见性两个方面,Java的同步机制都是围绕这两个方面来确保线程安全的。
关键字volatile主要使用的场合是在多个线程中可以感知实例变量被修改,并且可以获得最新的值使用,也就是多线程读取共享变量时可以获得最新值使用。
关键字volatile提示线程每次从共享内存中读取变量,而不是私有内存中读取,这样就保证了同步数据的可见性。但是要注意的是:如果修改实例变量中的数据
例如:i++,也就是i=i+1,则这样的操作其实并不是一个原子操作,也就是非线程安全的。表达式i++操作步骤分解如下:
1)从内存中取出i的值。
2)计算i的值;
3)将i的值写到内存中。
假如在第2步计算值得时候,另外一个线程也修改i的值,name这个时候就会出现脏读数据。解决的办法就是使用synchronized关键字。所以说volatile本身并不处理数据的
原子性,而是强制对数据的读写及时的影响到主内存中。
每一个优秀的人,都有一段沉默的时光。不抱怨,不诉苦,最后度过那段感动自己的日子。