volatile可见性及非原子性验证
大约 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、写回这个值
解决办法:使用原子类来解决原子性问题。

原子类为什么这么高级
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类是一个很特殊的存在。