线程安全-可见性

2019-05-06  本文已影响0人  三不猴子
共享变量在线程间不可见的原因
  1. 使用synchronized的来保证可见性

使用synchronized的两条规定:

  1. volatile 来实现可见性
    通过加入内存屏障和禁止重拍讯优化来实现可见性。

也就是说使用volatile关键字在读和写操作时都会强迫从主内存中获取变量值。

下图是使用volatile写操作的示意图

image

使用volatile写操作前会插入一条StoreStore指令来禁止在volatile写之前的普通写对volatile写的指令重排序优化,在写之后会插入一条StoreLoad屏障指令来防止上面的volatile写操作和下面可能有的读或者写进行指令重排序。

下图是volatile读操作示意图

image
@Slf4j
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 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
    }
}

我们多次运行个这段代码,发现结果并不是我们预期5000,volatile只能保证可见性并不能保证原子性。

通常来说使用volatile需要具备两个条件

所以volatile非常适合用作状态标记量,比如做为线程是否被初始化。还有就是用double check 我之前的博客就提到的单例模式中就使用了volatile来做double check 双重检查实现单例。


image
上一篇下一篇

猜你喜欢

热点阅读