美文网首页
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