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关键字的主要内容,如果未能解决你的问题,请参考以下文章

深度解析volatile关键字(保证够全面)❤❤

java volatile关键字

volatile关键字是什么意思

Java volatile关键字解惑

C语言 volatile 关键字在编译优化过程中有何作用

volatile关键字是不是能保证线程安全