2019独角兽企业重金招聘Python工程师标准>>>
volatile的作用是使变量在多个线程间可见
1.死循环
public class PrintInfo implements Runnable {private boolean statu = true;public boolean isStatu() {return statu;}public void setStatu(boolean statu) {this.statu = statu;}public void printInfo() {try {while(statu == true){System.out.println("The Thread Name Is " + Thread.currentThread().getName());Thread.sleep(1000);Thread.sleep(1000);}} catch (Exception e) {e.printStackTrace();}}@Overridepublic void run() {printInfo();} }
public class Run {public static void main(String[] args){
PrintInfo printInfo = new PrintInfo(); printInfo.printInfo(); System.out.println("Try To Stop The Thread"); printInfo.setStatu(false);
}
}
运行结果:
2.解决同步死循环
将一中的代码Run类修改为
public class Run {public static void main(String[] args) throws InterruptedException {PrintInfo printInfo = new PrintInfo();Thread thread = new Thread(printInfo);thread.start();Thread.sleep(1000);System.out.println("Try To Stop The Thread");printInfo.setStatu(false);} }
运行结果:
但当上面的实例代码运行在 -server服务器模式的64位的JVM上时,会出现死循环,解决的办法是使用volatile关键字.
volatile关键字的作用是强制从公共堆栈中取得变量的值,而不是从线程使用数据栈中取得变量的值
3.解决异步死循环
将1中的代码PrintInfo修改为
public class PrintInfo implements Runnable {volatile private boolean statu = true;public boolean isStatu() {return statu;}public void setStatu(boolean statu) {this.statu = statu;}public void printInfo() {try {while(statu == true){System.out.println("The Thread Name Is " + Thread.currentThread().getName());Thread.sleep(1000);}} catch (Exception e) {e.printStackTrace();}}@Overridepublic void run() {printInfo();} }
运行结果:
4.关键字synchronized与volatile的区别
使用volatile关键字增加了实例变量在多个线程之间的可见性,但是volatile关键字的缺点是不支持原子性.
1).关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,二synchronized可以修饰方法,以及代码块.随着JDK新版本的发布,synchronized关键字的执行效率得到很大提升,在开发中使用synchronized关键字的比例还是比较大.
2).多线程访问volatile不会发生阻塞,二synchronized回出现阻塞
3).volatile能保证数据的可见性.但不能保证原子性,而synchronized可以保证原子性,也可以间接保证可见性,以内他会将使用内存和公共内存中的数据做同步.
4).关键字volatile解决的是变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间访问资源的同步性,
线程安全包含原子性和可见性两个方面,JAVA的同步机制都是围绕这两个方面来确保线程安全的.
5.volatile的非原子特性
public class VolatileThread extends Thread{volatile public static int count ;private static void sumCount(){for(int i = 0 ; i < 10 ; i ++){count ++;}System.out.println("count = " + count);}@Overridepublic void run() {sumCount();} }
public class Run {public static void main(String[] args){VolatileThread[] volatileThreads = new VolatileThread[100];for(int i = 0 ; i < 10 ; i ++){volatileThreads[i] = new VolatileThread();}for(int i = 0 ; i < 10 ; i ++){volatileThreads[i].start();}} }
运行结果:
更改VolatileThread类代码为:
public class VolatileThread extends Thread{volatile public static int count ;synchronized private static void sumCount(){for(int i = 0 ; i < 10 ; i ++){count ++;}System.out.println("count = " + count);}@Overridepublic void run() {sumCount();} }
运行结果:
关键字volatile主要使用的场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取共享变量时可以获取最新值使用.
关键字volatile提示线程每次从共享内存中读取变量,而不是从私有内存中读取,这样就保证了同步数据的可见性,但需要注意的是:如果修改实例变量中的数据,比如i++,这样的操作其实并不是一个院子操作,也就是非线程安全的.
表达式i++的操作不走分解如下:
1).从内存中获取i的值,
2).计算i的值,
3).将计算后的值存储到内存中.
如果在第二步时,另一个线程也修改了i的值,那么这个时候就会出现脏数据,解决的办法就是使用synchronized关键字,所以volatile本身并不处理数据的原子性,而是强制对数据的读写及时影响到主内存.