volatile
volatile 轻量级的同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
JMM 内存模型
JVM运行程序的实体是线程,每个线程创建,JVM实体都会为其创建一个工作内存(栈空间),工作内存是每个线程的私有的数据区域,而java内存模型中规定所有的变量都存储在主内存中,主内存是共享的内存区域,所有线程都可以访问,但线程对变量的操作必须在工作内存中进行,首先要将变量从主内存中拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回到主内存。
硬盘<内存<缓存<CPU
线程将值写回主物理内存,及时通知其他线程,叫 JMM 内存模型的可见性
volatile 可见性代码验证
public class Test01 {
public static void main(String[] args) {
MyData myData = new MyData();
new Thread(() -> {
try {
//休眠3秒钟
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
//修改myData的值
myData.change();
System.out.println(Thread.currentThread().getName() + "修改了myData的值为:" + myData.num);
}, "AAA线程").start();
while (myData.num == 0) {
}
System.out.println("all thread is over......");
}
}
class MyData {
//volatile修饰变量保证了可见性
volatile int num = 0;
public void change() {
this.num = 1;
}
}
volatile 不保证原子性代码验证
public class Test01 {
public static void main(String[] args) {
MyData myData = new MyData();
CountDownLatch countDownLatch = new CountDownLatch(10);
//10个线程,每个线程执行1000次 i++ 操作
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
myData.increment();
}
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//正确的数据应该是10000,但是得到的数据却小于10000,原因是多线程情况下出现了写覆盖
System.out.println(myData.num);
}
}
class MyData {
volatile int num = 0;
public void increment() {
num++;
}
}
volatile 不保证原子性的解决方案
public class Test01 {
public static void main(String[] args) {
MyData myData = new MyData();
CountDownLatch latch = new CountDownLatch(20);
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
myData.increment();
}
latch.countDown();
}, String.valueOf(i)).start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(myData.atomicInteger);
}
}
class MyData {
//AtomicInteger保证了原子性操作
AtomicInteger atomicInteger = new AtomicInteger();
public void increment() {
atomicInteger.getAndIncrement();
}
}