volatile关键字
Posted moruoxuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了volatile关键字相关的知识,希望对你有一定的参考价值。
关键字volatile的主要作用是使变量在多个线程间可见
一、volatile关键字与死循环
如果不是在多继承的情况下,使用Thread类和实现Runnnable接口在取得程序运行的结果上并没有什么太大的区别。如果一旦出现“多继承”的情况下,则用实现Runnable接口的方式来处理多线程的问题就是很有必要的。
public class PringString { private boolean isContinuePrint = true; public boolean isContinuePrint() { return isContinuePrint; } public void setContinuePrint(boolean continuePrint) { isContinuePrint = continuePrint; } public void printStringMethod(){ try { while(isContinuePrint == true){ System.out.println("run printStringMethod threadName="+Thread.currentThread().getName()); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { PringString p = new PringString(); p.printStringMethod(); System.out.println("stop stopThread" + Thread.currentThread().getName()); p.setContinuePrint(false); }
运行结果:
run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main
...
结论:程序运行开始后,没有停下来,原因是main线程一直在处理while()循环,导致不能执行后面的代码
二、解决死循环办法
public class PringString implements Runnable {
private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setContinuePrint(boolean continuePrint) {
isContinuePrint = continuePrint;
}
@Override
public void run() {
System.out.println("进入run了");
while (isContinuePrint == true) {
}
System.out.println("线程结束了");
}
public static void main(String[] args) {
try {
PringString p = new PringString();
new Thread(p).start();
Thread.sleep(100);
p.setContinuePrint(false);
System.out.println("已赋值false");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
进入run了
已赋值false
如果运行在-server服务器模式中的64bit的JVM上时,会出现死循环,解决办法是使用volatile
原因:再启动PringString.java线城时,变量private boolean isContinuePrint = true;存在于公共堆栈及线程私有堆栈中。在JVM被设置为-server模式时为了线程的运行效率,线程一直在私有堆栈中取得isContinuePrint的值为true,而代码 p.setContinuePrint(false); 虽然被执行,更新的确实公共堆栈中的isContinuePrint变量值为false,所以一直是死循环的状态。
这个问题其实就是私有堆栈中的值和公共堆栈中的值不同步造成的,解决这样的问题就要使用volatile关键字了,它主要的作用就是当线程访问isContinuePrint这个变量时,强制性从公共堆栈中进行取值
关键字volatile的作用是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值
解决方法
public class PringString implements Runnable {
volatile private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setContinuePrint(boolean continuePrint) {
isContinuePrint = continuePrint;
}
@Override
public void run() {
System.out.println("进入run了");
while (isContinuePrint == true) {
}
System.out.println("线程结束了");
}
public static void main(String[] args) {
try {
PringString p = new PringString();
new Thread(p).start();
Thread.sleep(100);
p.setContinuePrint(false);
System.out.println("已赋值false");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
进入run了 已赋值false 线程结束了
四、volatile与synchronized的区别
1)关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,而synchronized可以修饰方法,以及代码块。随着JDK新版本的发布,synchronized关键字在执行效率上得到很大的提升,在开发中使用synchronized关键字的比率还是比较大的
2)多线程访问volatile不会发生阻塞,而synchronized会出现阻塞
3)volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接的保证可见性,因为他会将私有的内存和公共的内训中数据做同步
4)关键字volatile解决的是变量在多线程之间的可见性;而synchronized关键字解决的是多个线程之间的访问资源同步性
五、volatile非原子的特性
关键字volatile主要使用的场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取共享变量时可以获取最新的值使用
关键字volatile提示线程每次从共享内存中读取变量,而不是从私有内存中读取,这样就保证了同步数据的可见性,但是在这里需要注意的是:如果修改实例变量中的数据,比如i++,也就是i=i+1,则这样的操作其实并不是一个原子的操作,就是非线程安全的,表达式i++的操作步骤分解如下:1)从内存中取出I的值;2)计算i的值;3)将i的值写到内存中。假如在第2步计算值的时候,另外一个线程也修改i的值,那么这个时候就会出现脏数据,解决办法是使用synchronized关键字。所以说volatile本身并不处理数据的原子性,而是强制对数据的读写及时影响到主内存中。
六、i++操作可以使用原子类进行
除了i++操作在使用synchronized关键字实现同步外,还可以使用AtomicInteger原子类进行实现。
原子操作是不能分割的整体,没有其他线程能够中断或检查正在原子操作中的变量。一个原子(atomic)类型就是一个原子操作可用的类型,它可以在没有锁的情况下做到线程安全。
原子类也并不完全线程安全,原子类在具有逻辑想的情况下输出结果也具有随机性
以上是关于volatile关键字的主要内容,如果未能解决你的问题,请参考以下文章