volatile可见性及非原子性验证

HeJin大约 2 分钟JavaJUC并发编程

volatile可见性

public class JmmDemo {
    // volatile保证可见性.不加,程序就会死循环
    private volatile static int num = 0;
    // main线程
    public static void main(String[] args) throws InterruptedException {
        // 线程1
        new Thread(() -> {
            while (num == 0){
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        num = 1;
        System.out.println(num);
    }
} 

结果:

1
    
Process finished with exit code 0

volatile不保证原子性

原子性:不可分割。

线程A在执行任务的时候,是不能被打扰的,也不能被分割。要么同时成功,要么同时失败。

/**
 * @Desc 不保证原子性
 * @Author HeJin
 * @Date 2021/3/13 9:58
 */
public class VolatileDemo {
    private static int num = 0;
    public static void add(){
        num++;
    }
    public static void main(String[] args) {
        // 理论上num结果为20000
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

结果:

main 19240
    
Process finished with exit code 0

加上volatile

// volatile不保证原子性
private volatile static int num = 0;

结果:

main 19959
    
Process finished with exit code 0

这就说明了volatile不保证原子性。


加锁可以解决原子性问题。那么如果不加lock锁和synchronized关键字,怎么保证原子性?


程序class文件反编译后的代码:

Compiled from "VolatileDemo.java"
public class com.hejin.volatile1.VolatileDemo {
  public com.hejin.volatile1.VolatileDemo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
  // num++
  public static void add();
    Code:
       0: getstatic     #2                  // Field num:I
       3: iconst_1
       4: iadd
       5: putstatic     #2                  // Field num:I
       8: return
  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iload_1
       3: bipush        20
       5: if_icmpgt     29
       8: new           #3                  // class java/lang/Thread
      11: dup
      12: invokedynamic #4,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
      17: invokespecial #5                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
      20: invokevirtual #6                  // Method java/lang/Thread.start:()V
      23: iinc          1, 1
      26: goto          2
      29: invokestatic  #7                  // Method java/lang/Thread.activeCount:()I
      32: iconst_2
      33: if_icmple     42
      36: invokestatic  #8                  // Method java/lang/Thread.yield:()V
      39: goto          29
      42: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      45: new           #10                 // class java/lang/StringBuilder
      48: dup
      49: invokespecial #11                 // Method java/lang/StringBuilder."<init>":()V
      52: invokestatic  #12                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
      55: invokevirtual #13                 // Method java/lang/Thread.getName:()Ljava/lang/String;
      58: invokevirtual #14                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      61: ldc           #15                 // String
      63: invokevirtual #14                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      66: getstatic     #2                  // Field num:I
      69: invokevirtual #16                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      72: invokevirtual #17                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      75: invokevirtual #18                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      78: return
  static {};
    Code:
       0: iconst_0
       1: putstatic     #2                  // Field num:I
       4: return
}

发现num++并不是原子性操作:

  • 1、获得这个值
  • 2、加1
  • 3、写回这个值

解决办法:使用原子类来解决原子性问题。

image-20210325164613148
image-20210325164613148

原子类为什么这么高级

public class VolatileDemo {
    // 原子类的Integer
    private volatile static AtomicInteger num = new AtomicInteger(0);
    
    public static void add(){
        // AtomicInteger +1 原理:CAS
        num.getAndIncrement();
    }
    
    public static void main(String[] args) {
        // 理论上num结果为20000
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
            for (int j = 0; j < 1000; j++) {
            	add();
    		}
    		}).start();
    	}
        while (Thread.activeCount() > 2){
        	Thread.yield();
        }
    	System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

结果:

main 20000
    
Process finished with exit code 0

java.util.concurrent.atomic这些原子类的底层都直接和操作系统挂钩。在内存中修改值。Unsafe类是一个很特殊的存在。