版权声明:送人玫瑰,手有余香,一分也是爱 https://blog.csdn.net/m0_37156901/article/details/85863545
可见性
是一个线程对内存的修改可以及时的被其他线程看到。
不可见的原因(Java内存模型原理)
对于可见性-JVM提供synchronize 和 volatile
-
- synchronize Atomic中讲到的四种修饰,类,块,静态方法。。。
- synchronize Atomic中讲到的四种修饰,类,块,静态方法。。。
-
- volatile原理通俗理解
volatile变量值在每次被线程访问时,都强迫从主内存中读取该变量的值。
而当该变量发生变化的时候,又强迫线程将最新的值刷新到主内存。这样任何时候,线程总能看到该变量的最新值。
- volatile原理通俗理解
下面解释内存屏障和禁止重排序的实现
所有这些都是在CPU指令级别进行操作的
那么,如果使用volatile修饰计数器的值,是否是线程安全的?看代码。
package com.mmall.concurrency.atomicDemo.count;
import com.mmall.concurrency.annotations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@ThreadSafe
public class VolatileCount {
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
public static volatile int count = 0;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count);
}
private static void add() {
count++;
// 1、count
// 2、+1
// 3、count
}
}
结论,volatile不是线程安全的
不适合计数,不是原子安全的,
- 因为在执行 count++ 这个操作的时候是进行了三步,
- 先从主存取出值
- 加 1 操作
- 再写回主存
单线程没问题,多线程中可能两个线程同时取最新的值,加 1 再放回,同时操作可能漏掉一些,所以每次执行都比预期的值小。
volatile的使用的两个条件
-
- 对变量的写操作不依赖于当前值
-
- 该变量没有包含在具有其他变量的不必要的式子中,因此volatile适合做状态标记量,
volatile使用的场景1 :状态标记
volatile boolean inited = false;
// thread 1
context = loadContext();
inited = true;
//thread 2, 判断状态,线程1初始化完成后,再在初始化基础之上做其他事
while( !inited){
sleep();
}
do
SomethingWithConfig(context);
volatile使用场景2:doubleCheck检查两次
这个之后做解释,更新快,持续关注哦