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