保证可见性
加上volatile
java
package com.mystpet.JMM;
import java.util.concurrent.TimeUnit;
public class JMMDemo {
// 不加 volatile 程序就会死循环
// 加 volatile 可以保证可见性
private volatile static int num=0;
public static void main(String[] args) {
new Thread(()->{
while(num==0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
num=1;
System.out.println(num);
}
}不保证原子性
原子性 : 不可分割
线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败。
java
package com.mystpet.JMM;
public class VDemo02 {
private volatile static int num=0;
public static void add(){
num++; // 不是一个原子性操作
}
public static void main(String[] args){
for (int i = 1; i <= 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
add();
}
}) .start();
}
while(Thread.activeCount()>2){ // main 和 gc 默认执行
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+" "+num);
}
} 理论20000第一步: 线程 A 读取
num,拿到100。几乎在同一微秒,线程 B 也读取了num,拿到的也是100。第二步: 线程 A 把 100 加 1 变成了 101。线程 B 也把 100 加 1 变成了 101。
第三步: 线程 A 把
101写回主内存。紧接着,线程 B 也把101写回主内存。
如果不加 lock 和 synchronized ,怎么样保证原子性

使用原子类,解决 原子性问题

java
package com.mystpet.JMM;
import java.util.concurrent.atomic.AtomicInteger;
public class VDemo02 {
private volatile static AtomicInteger num=new AtomicInteger(0);
public static void add(){
num.getAndIncrement();
}
public static void main(String[] args){
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);
}
}这些类的底层都直接和操作系统挂钩!在内存中修改值!Unsafe类是一个很特殊的存在!
指令重排
什么是 指令重排:你写的程序,计算机并不是按照你写的那样去执行的。
源代码–>编译器优化的重排–> 指令并行也可能会重排–> 内存系统也会重排—> 执行
==处理器在进行指令重排的时候,考虑:数据之间的依赖性!==
java
int x = 1; // 1
int y = 2; // 2
x = x + 5; // 3
y = x + x; // 4
我们所期望的:1234 2134 1324 不可能是 4213可能造成影响的结果: a b x y 这四个值默认都是 0;
| 线程A | 线程B |
|---|---|
| x=a | y=b |
| b=1 | a=2 |
正常的结果: x = 0;y = 0;但是可能由于指令重排
| 线程A | 线程B |
|---|---|
| b=1 | a=2 |
| x=a | y=b |
| 指令重排导致的诡异结果: x = 2;y = 1; |
volatile可以避免指令重排:
内存屏障。CPU指令。作用:
1、保证特定的操作的执行顺序!
2、可以保证某些变量的内存可见性 (利用这些特性volatile实现了可见性)

Volatile 是可以保持 可见性。不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!