• Java volatile关键字


    volatile关键字的作用

      volatile关键字是Java虚拟机提供的最轻量级的同步机制,volatile具有可见性和有序性,但是,不具有原子性特性

      Java中提供的操作运算符不具有原子性。

      看下面例子:

    public class Main {
    
        public static volatile int race = 0;
        private static final int THREAD_COUNT = 10;
    
        public static void increase() {
            race++;
        }
    
        public static void main(String[] args) {
            final CountDownLatch downLatch = new CountDownLatch(THREAD_COUNT);
    
            Runnable runnable = () -> {
                for (int i = 0; i < 10000; i++) {
                    increase();
                }
    
                downLatch.countDown();
            };
    
            Thread[] threads = new Thread[THREAD_COUNT];
            for (int i = 0; i < THREAD_COUNT; i++) {
                threads[i] = new Thread(runnable);
                threads[i].start();
            }
    
            try {
                downLatch.await();
            } catch (InterruptedException exception) {
                exception.printStackTrace();
            }
    
            System.out.println("end race = " + race);
        }
    
    }
    

       结果:

        期望:100000

        输出:end race = 41573

      输出结果不到100000,不是期望值,通过javap反编译:

    Compiled from "Main.java"
    public class example.Main {
      public static volatile int race;
    
      public example.Main();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      public static void increase();
        Code:
           0: getstatic     #2                  // Field race:I
           3: iconst_1
           4: iadd
           5: putstatic     #2                  // Field race:I
           8: return
    
      public static void main(java.lang.String[]);
        Code:
           0: new           #3                  // class java/util/concurrent/CountDownLatch
           3: dup
           4: bipush        10
           6: invokespecial #5                  // Method java/util/concurrent/CountDownLatch."<init>":(I)V
           9: astore_1
          10: aload_1
          11: invokedynamic #6,  0              // InvokeDynamic #0:run:(Ljava/util/concurrent/CountDownLatch;)Ljava/lang/Runnable;
          16: astore_2
          17: bipush        10
          19: anewarray     #7                  // class java/lang/Thread
          22: astore_3
          23: iconst_0
          24: istore        4
          26: iload         4
          28: bipush        10
          30: if_icmpge     58
          33: aload_3
          34: iload         4
          36: new           #7                  // class java/lang/Thread
          39: dup
          40: aload_2
          41: invokespecial #8                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
          44: aastore
          45: aload_3
          46: iload         4
          48: aaload
          49: invokevirtual #9                  // Method java/lang/Thread.start:()V
          52: iinc          4, 1
          55: goto          26
          58: aload_1
          59: invokevirtual #10                 // Method java/util/concurrent/CountDownLatch.await:()V
          62: goto          72
          65: astore        4
          67: aload         4
          69: invokevirtual #12                 // Method java/lang/InterruptedException.printStackTrace:()V
          72: getstatic     #13                 // Field java/lang/System.out:Ljava/io/PrintStream;
          75: new           #14                 // class java/lang/StringBuilder
          78: dup
          79: invokespecial #15                 // Method java/lang/StringBuilder."<init>":()V
          82: ldc           #16                 // String end race =
          84: invokevirtual #17                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          87: getstatic     #2                  // Field race:I
          90: invokevirtual #18                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
          93: invokevirtual #19                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          96: invokevirtual #20                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          99: return
        Exception table:
           from    to  target type
              58    62    65   Class java/lang/InterruptedException
    
      static {};
        Code:
           0: iconst_0
           1: putstatic     #2                  // Field race:I
           4: return
    }
    

       着重看increase()方法:

    public static void increase();
        Code:
           0: getstatic     #2                  // Field race:I
           3: iconst_1
           4: iadd
           5: putstatic     #2                  // Field race:I
           8: return
    

       在race变量读取和写入新值之间有“++”运算符操作,而Java运算符是不具有原子性的,导致race变量值出错。

      这不代表volatile的同步机制有错,只是因为volatile不具有原子性,volatile关键字修饰的变量只能保证可见性和有序性,在不能保证原子性操作的场景,依然需要使用synchronized、java.util.concurrent中的锁和原子类来保证原子性。

      volatile关键字在下面场景可以正常使用:

    • 运算结果不依赖当前值,或者保证只有一条线程修改变量值。
    • volatile修饰的变量不需要和其他状态变量参与不变约束。
  • 相关阅读:
    【前端开发】vue子项目打包一个组件供另一个项目使用教程
    【前端开发】基于flow-editor-vue库改造的流程设计器,审批流程引擎前端教程
    【前端开发】基于logicFlow可视化流程库改造的流程引擎教程
    知识蒸馏:Distillation
    浮点神经网络vs二值神经网络
    Representation Learning 表征学习
    mybatis plus 追加where 函数
    20211012 MapStruct
    20211012 Dubbo 的 SPI 和 Adaptive
    20210916 小马哥讲 Spring AOP
  • 原文地址:https://www.cnblogs.com/naray/p/15440130.html
Copyright © 2020-2023  润新知