美文网首页
volatile修饰符使用场景

volatile修饰符使用场景

作者: TryCatch菌 | 来源:发表于2019-04-15 17:09 被阅读0次

volatile修饰符使用场景

在多线程并发编程中,经常使用的是线程锁,例如 synchronized,其实在某些特定场景下,可以使用volatile这个轻量级的 “synchronized” 来实现。volatile 它在多线程编程中能保共享变量的可见性。可见性的意思按照并发编程的艺术这边书上描述的就是:“当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值”。

书面说法的volatile使用场景:

  • 运算结果并不依赖变量的当前值(即结果对产生中间结果不依赖),或者能够确保只有单一的线程修改变量的值
  • 变量不需要与其它的状态变量共同参与不变约束

这里就不画什么内存图,做什么底层原理描述了,书上的描述绝对比我自己的口水话清楚,这里算是看并发编程的艺术这本书的时候,因为volatile这个修饰符用的场景比较少,做个记录,下面用代码跑一下。

使用场景

synchronized不通,在多线程的操作下,不保证变量的原子性,也就是说在多线程下只保证能取出当前最新值,而不保证在多线程同时操作变量的那一瞬间是唯一的,原子性的操作。

  • volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
package com.wzh.demo.concurrentProgrammingArt.lock;

/**
 * @desc: volatile修饰符测试
 * @author: wzh
 * @date: 2019年4月14日
 */
public class VolatileTest implements Runnable{
    private  boolean running = true;
      
    public boolean isRunning() {
        return running;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    @Override
    public void run() {
        while(running){
            
        }
        System.out.println("子线程"+Thread.currentThread().getName()+"停止");
        
    }
    
    @SuppressWarnings("static-access")
    public static void main(String[] args) throws InterruptedException {
          VolatileTest task = new VolatileTest();
        //启动子线程
        new Thread(task).start();
        Thread.currentThread().sleep(1000);
        task.setRunning(false);
        System.out.println("主线程停止");
      }

    
}

这段代码运行会发现子线程不会停止,因为没有错操作去内存中都数据,子线程中的running一直是true

代码修改一下,running 加volatile修饰符

package com.wzh.demo.concurrentProgrammingArt.lock;

/**
 * @desc: volatile修饰符测试
 * @author: wzh
 * @date: 2019年4月14日
 */
public class VolatileTest implements Runnable{
    private volatile boolean running = true;
      
    public boolean isRunning() {
        return running;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    @Override
    public void run() {
        while(running){
        }
        System.out.println("子线程"+Thread.currentThread().getName()+"停止");
        
    }
    
    @SuppressWarnings("static-access")
    public static void main(String[] args) throws InterruptedException {
          VolatileTest task = new VolatileTest();
        //启动子线程
        new Thread(task).start();
        Thread.currentThread().sleep(1000);
        task.setRunning(false);
        System.out.println("主线程停止");
      }

    
}

运行结果

主线程停止
子线程Thread-0停止

总结

  • volatile无法实现原子性,只能实现可见性
  • 当要访问的变量已在synchronized代码块中,或者为常量时,没必要使用volatile。
  • 由于使用volatile屏蔽掉了JVM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字
  • 在需要同步的时候,第一选择应该是synchronized关键字,这是最安全的方式,尝试其他任何方式都是有风险的。尤其在、jdK1.5之后,对synchronized同步机制做了很多优化,如:自适应的自旋锁、锁粗化、锁消除、轻量级锁等,使得它的性能明显有了很大的提升。
  • 当且仅当满足以下所有条件时,才应该使用volatile变量:
    1. 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。
    2. 该变量没有包含在具有其他变量的不变式中,防止影响其他变量
    3. 防止代码重排

相关文章

网友评论

      本文标题:volatile修饰符使用场景

      本文链接:https://www.haomeiwen.com/subject/dehpwqtx.html