1.共享变量在线程间不可见的原因
<1> 线程交叉执行
<2> 重排序结合线程交叉执行
<3> 共享变量更新后的值没有在工作内存与主内存间及时更新
2.synchronized
规定1:线程解锁前,必须把共享变量的最新值刷新到主内存
规定2:线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意:加锁和解锁是同一把锁)
3.volatile
通过加入内存屏障和禁止重排序优化来实现,即
- 1 对volatile变量写操作时,会在写操作后加入一条store屏障指令,将本地内存中的共享变量值刷新到主内存
- 2 对volatile变量读操作时,会在读操作前加入一条load屏障指令,从主内存中读取共享变量
volatile应用于计数问题
public class CountExample4 {
//请求总数
public static int clientTotal=5000;
//同时并发执行的线程数
public static int threadTotal=200;
public static volatile int count=0;
public static void main(String[] args) throws InterruptedException {
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++;
}
}
此时,并不能保证最后的结果为5000,因为在执行count++时,假设有两个线程,
第一步读取count值,都是从主内存中获取值,两个线程没有问题;
第二步,对count值进行自增操作;
第三步,将最新的count值写回到主内存中;
如果两个线程同时执行2,3步,可能会减少count++的次数。
所以volatile不适用计数场景,不具备原子性。
volatile使用的场景:
1.作为状态标记量(对变量的写操作不依赖于当前值;该变量没有包含在具有其他变量的式子中)
2.还适用double check场景
网友评论