[并发] 4 线程安全性-可见性

2019-11-03  本文已影响0人  LZhan

1.共享变量在线程间不可见的原因

<1> 线程交叉执行
<2> 重排序结合线程交叉执行
<3> 共享变量更新后的值没有在工作内存与主内存间及时更新

2.synchronized

规定1:线程解锁前,必须把共享变量的最新值刷新到主内存
规定2:线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意:加锁和解锁是同一把锁)

3.volatile

通过加入内存屏障禁止重排序优化来实现,即

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.作为状态标记量
(对变量的写操作不依赖于当前值;该变量没有包含在具有其他变量的式子中)

image.png
2.还适用double check场景

相关博客链接:https://www.jianshu.com/p/ccfe24b63d87

上一篇 下一篇

猜你喜欢

热点阅读