Wow4j Wow4j
首页
个人使用说明书
后端开发
前端开发
测试开发
运维开发
大数据开发
产品&UI交互
团队管理
软技能
他山之石
开源产品
敬请期待
GitHub (opens new window)
首页
个人使用说明书
后端开发
前端开发
测试开发
运维开发
大数据开发
产品&UI交互
团队管理
软技能
他山之石
开源产品
敬请期待
GitHub (opens new window)
  • 概要
  • 面试八股文

    • 概要
    • 共享变量在线程中的可见性问题分析
      • 导致共享变量在线程间不可见的原因:
        • 可见性-synchronized
        • 可见性-volatile
        • 问题:只利用volatile来修饰计数器,是否能够保证计数器是原子操作的了?答案是不能的。
    • 如何设计延时消息
    • 草稿
  • 服务端小技巧合集

  • Java基础

  • MySQL 相关

  • Redis 最佳实践指南

  • 文本搜索Elasticsearch

  • Kafka 最佳实践指南

  • 网络相关

  • 架构相关

  • 监控告警

  • 防爬风控

  • 稳定性 checklist

  • 效能工具

  • 后端开发
  • 面试八股文
timchen525
2023-02-01

共享变量在线程中的可见性问题分析

​

# 导致共享变量在线程间不可见的原因:

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

# 可见性-synchronized

JVM中关于synchronized的两条规定:

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

# 可见性-volatile

通过加入内存屏障和禁止重排序优化来实现(对于被volatile变量的操作都是直接针对主内存)

  1. 对volatile变量写操作时,会在写操作后加入一条store屏障指令,将本地内存中的共享变量值刷新到主内存
  2. 对volatile变量读操作时,会在读操作前加入一条load屏障指令,从主内存中读取共享变量

图片未正常显示 https://img-blog.csdn.net/20180524215819277

https://img-blog.csdn.net/20180524215819277

# 问题:只利用volatile来修饰计数器,是否能够保证计数器是原子操作的了?答案是不能的。

比如如下代码:(在count前加入volatile修饰)

public class AtomicExample7 {

    //  请求总数
    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 (InterruptedException e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);

    }

    private static void add() {
        count ++;
    }
}

输出:

[main] INFO com.example.concurrent.example.count.AtomicExample7 - count:4977、

分析:

在上面的代码中的计数变量count前加入了volatile保证变量count的读和写能够及时的更新到内存中,但是运行出来的结果仍然是非线程安全的,原因是在cout++的操作,可以分为三步:

  1. 取得count的值;
  2. 对count进行加1操作;
  3. 写count的值到主内存中;

上面这三步合起来就不是线程安全的,比如,两个线程可能同时取得count的值,然后,同时进行加1操作,并写回主存,这样就丢掉了一次加1的操作。 即:voliatile不适用于计数的场景。 ​

上次更新: 2023/02/06, 09:35:40
概要
如何设计延时消息

← 概要 如何设计延时消息→

Theme by Vdoing | Copyright © 2022-2023 timchen525 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×