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修饰的变量不需要和其他状态变量参与不变约束。

 

posted @ 2021-10-23 11:24  naray  阅读(95)  评论(0编辑  收藏  举报