浅谈volatile

多线程操作变量过程

由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(或者栈空间),工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存(物理内存),主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读写等)必须在工作内存中进行,首先要将变量从主内存拷贝到自己的工作内存中,然后对变量进行操作,操作完成后把变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存的变量拷贝副本,因此不同的线程间无法访问对方的工作内存,线程间的通信必须通过主内存来完成,简要的访问过程如下图:
在这里插入图片描述

JMM(Java内存模型)对于线程同步的要求
  • 可见性
  • 原子性
  • 有序性
volatile的同步机制
  • 保证可见性
  • 不保证原子性
  • 禁止指令重排

总结:volatile的禁止指令重排可以保证有序性,相较于JMM来说,除了原子性是硬伤外,另外两项,volatile都可以保证,所以说 volatile是低配版的synchronized,volatile是Java虚拟机提供的轻量级的同步机制。

保证可见性的证明:

可见性说明: 主内存中的变量修改,本地内存第一时间得到通知并修改本地内存中的值。

class MyDate {
    int num = 0;
    //变量增加1
    public void numAddOne() {
        ++num;
    }
}
public class T {
    public static void main(String[] args) {
        MyDate myDate = new MyDate();
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3); //myThread线程休眠3秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myDate.numAddOne();
            System.out.println("current thread is:" + Thread.currentThread().getName() + ",num is:" + myDate.num);
        }, "myThread").start();

        //变量值为0时,线程阻塞在这里
        while (myDate.num == 0) {

        }
        System.out.println("current thread is:" + Thread.currentThread().getName() + ",num is:" + myDate.num);
    }
}

运行结果:
在这里插入图片描述
结果分析:myThread线程对变量num操作后,更新回主内存,但是main线程没有得到num值修改了的通知,导致main线程中的num还是0,所以程序阻塞在while循环处,无法打印后面的内容。
修改方案:在变量num前加上volatile,保证num的可见性,可以得到如下的运行结果:
在这里插入图片描述

不保证原子性的证明:

原子性说明: 一个操作可以分为多个步骤,原子性要求:只有当整个操作都完成,才停止,非原子性则是,在完整操作的某个中间步骤,能够被停止,等CPU再次唤醒,然后继续执行。

class MyDate {
    volatile int num = 0;
    //变量增加1
    public void numAddOne() {
        ++num;
    }
}

public class T {
    public static void main(String[] args) {
        MyDate myDate = new MyDate();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    myDate.numAddOne();
                }
            }, i + "").start();
        }
        //保证创建的20个线程运行完毕
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println("compute finished, num is:" + myDate.num);
    }
}

运行结果:
在这里插入图片描述

结果分析:因为volatile无法保证原子性,所以导致某个线程写步骤停止在某个步骤上,而其他线程用了该值,而停止的线程被唤醒后,继续使用该值,导致了写覆盖的情况,导致了最终的结果不是200000,当然也有可能会出现结果是200000的情况。
修改方案:可以在numAddOne方法前加上synchronized,但是仅仅为了一个加一的操作就使用synchronized,着实有一些杀鸡用牛刀的感觉。实际使用的方案:volatile无法保证原子性,那么我们就是用整型原子(AtomicInteger),整型原子当然能保证原子性了,如此一来就能保证原子性了。修改后的代码如下:

class MyDate {
    volatile AtomicInteger num = new AtomicInteger();
    //变量增加1
    public void numAddOne() {
        num.getAndIncrement();
    }
}

public class T {
    public static void main(String[] args) {
        MyDate myDate = new MyDate();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    myDate.numAddOne();
                }
            }, i + "").start();
        }
        //保证创建的20个线程运行完毕
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println("compute finished, num is:" + myDate.num.get());
    }
}

运行结果:
在这里插入图片描述
通过AtomicInteger和volatile我们就保证了上例子中多线程操作的原子性,可见性,有序性,和使用synchronized相比,AtomicInteger和volatile的效率要高很多,synchronized一次只允许一个线程操作,而AtomicInteger和volatile允许多个线程一起操作,效率自然也要高很多。

本篇文章没有对有序性进行描述,因为有序性需要汇编的支持,大意:在代码编译的过程中,编译器与处理器会对指令做重排,以提高效率。在单线程中指令重排并不会影响操作的结果,但是多线程无法保证结果准确,因此我们需要告诉编译器和处理器,我们的多线程程序不需要重排,通过volatile可以做到禁止指令重排。本篇中解决原子性问题使用的是 AtomicInteger,但是并没有介绍为什么AtomicInteger就能保证原子性,所以在以后的文章中,我会写一篇来介绍,为什么AtomicInteger就能保证原子性,以及它是如何做到保证原子性的。

以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化中的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值