您的位置:首页 > 编程语言 > Java开发

java多线程之volatile关键字

2015-08-05 21:16 691 查看
在java线程并发处理中,关键字volatile的主要作用是使变量在多个线程间可见。那么volatile到底该怎么用了?我们首先来看一段代码:

public class MyThread1 implements Runnable {
private boolean istag = true;

public boolean isIstag() {
return istag;
}
public void setIstag(boolean istag) {
this.istag = istag;
}

public void print() {
try {
while (istag) {
System.out.println("print()线程名称是:"+Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (Exception e) {
}
}

@Override
public void run() {
print();
}
}

public class MyThread {
public static void main(String[] args) {
MyThread1 m=new MyThread1();
new Thread(m).start();
System.out.println("我要停止循环-->"+Thread.currentThread().getName());
m.setIstag(false);
}
}


此段代码如果运行,我们可以发现线程能正常停止。如果代码运行在-server服务器模式中64bit的JVM上时,会出现死循环。解决的方案是用volatile关键字。关键字volatile的作用是强制从公共堆栈取得变量的值,而不是从线程私有数据栈中取得变量的值。

我们将代码改造下:

public class MyThread1 extends Thread {
private volatile boolean istag = true;

public boolean isIstag() {
return istag;
}
public void setIstag(boolean istag) {
this.istag = istag;
}

@Override
public void run() {
System.out.println("进入run()====");
while (istag) {
System.out.println("线程名称是:"+Thread.currentThread().getName());
}
System.out.println("线程被停止====");
}
}




使用了volatile关键字增加了实例变量在多线程之间的可见性。但volatile关键字最致命的缺点是不支持原子性。

关键字synchronized跟volatile进行一下比较:

关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰变量,而synchronized可以修饰方法,以及代码块。随着JDK新版本的发布,synchronized关键字执行效率打打的提高,在开发中使用synchronized还是比较多的。
多线程访问volatile不会发生堵塞,而synchronized可能会出现堵塞。
volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做了同步。

关键字volatile解决的是变量在多个线程之间的可见性;而synchronized关键字解决的是多个线程之间访问资源的同步性。

volatile非原子的特性
public class MyThread1 extends Thread {
private volatile static int count;
public void addCount(){
for (int i = 0; i < 100; i++) {
count++;
}
System.out.println("count="+count);
}
@Override
public void run() {
addCount();
}
}

public class RunTest {
public static void main(String[] args){
MyThread1[] myArray=new MyThread1[100];
for (int i = 0; i < 100; i++) {
myArray[i]=new MyThread1();
}
for (int i = 0; i < 100; i++) {
myArray[i].start();
}
}
}




public class MyThread1 extends Thread {
private static int count;
//需要加static,才达到同步效果
public synchronized static void addCount(){
for (int i = 0; i < 100; i++) {
count++;
}
System.out.println("count="+count);
}
@Override
public void run() {
addCount();
}
}




关键字volatile主要使用的场合是在多线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取共享变量时可以获得最新的值。

关键字volatile提示线程每次从共享内存中读取变量,而不是从私有内存中读取,这样就保证了同步数据的可见性。但需要注意的是:如果修改实例变量中的数据,比如i++,也就是i=i+1,这样的操作并不是一个原子操作,也就是非线程安全的。表达式i++的实际操作步骤是:

从内存中读取i的值;
计算i的值;
将i的值写入到内存中。
所以说volatile本身并不处理数据的原子性,而是强制对数据读写及时影响到主内存的。

由上我们可以得出结论:
synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。通常来说,使用volatile必须具备以下2个条件:

  1)对变量的写操作不依赖于当前值

  2)该变量没有包含在具有其他变量的不变式中

  实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。

  事实上,我的理解就是上面的2个条件需要保证操作是原子性操作,才能保证使用volatile关键字的程序在并发时能够正确执行。



Java中使用volatile的场景:

package com.ztz.myThread;

public class Singleton {
private volatile static Singleton instance = null;

private Singleton() {
}
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class) {
if(instance==null)
instance = new Singleton();
}
}
return instance;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: