高性能编程之锁相关

声明:

        这篇文章是在学习网易云课堂后所做的笔记,主要知识点均来源于视频课程。如果需要了解更多信息请联系网易云课堂相关工作人员(萌萌小姐姐能不能加个鸡腿)。这是一个Java方向高级开发工程师系列课程,如果时间充足,后续会将课程内容以文章的形式整理分享。写的不好,希望大家批评指正。


1.几个锁相关的概念

        说到锁,有几个概念在面试中经常提到,这里做一个笔记。

        自旋锁:为了不放弃CPU执行时间,循环的使用CAS技术对数据进行尝试更新,直至成功。

        悲观锁:假定会发生冲突,同步所有对数据的相关操作,从读数据就开始上锁。

        乐观锁:假定不会发生冲突,在修改数据时如果发现数据和之前获取的不一致,则读最新数据,修改后重试修改。

       独享锁(写):给资源加上写锁,线程可以修改资源,其他线程不能再加锁(单写);

       共享锁(读):给资源加上读锁后只能读不能改,其他线程也只能加读锁,不能加写锁(多读)。

       可重入锁、不可重入锁:对资源的访问,只需要获取一次锁就可以多次访问,是可重入锁。不可重入锁则需要等锁释放之后才能获取锁,进而访问资源。

        公平锁、非公平锁:争抢锁的顺序,如果是按先来后到的顺序,则为公平锁。

2.synchronized同步关键字

       synchronized是java提供的同步关键字,简单便捷,属于最基本的线程通信机制,是基于对象监视器(Monitor,也叫管程)实现的。   java中的每个对象都与一个监视器(监视器的管理由具体的JVM实现)相关联,一个线程可以锁定或解锁,一次只有一个线程可以锁定监视器,试图锁定该监视器的任何其他线程都会被阻塞,直到它们可以获得该监视器上的锁为止。

        同步关键字不仅仅可以实现多线程之间的同步,还保证了可见性。

       通过synchronized加锁,符合上面提到的“可重入锁、独享锁、悲观锁”几个概念,这也是它的特性,在使用过程中要充分理解业务逻辑,合理使用synchronized关键字。

        synchronized锁的范围:类锁、对象锁、锁消除、锁粗化。

       什么是锁消除?锁消除就是如果某代码块在任何情况下只有一个线程操作(比如:对方法内局部变量的读写操作),即使代码中写了synchronized关键字,jit优化也可能把它消除掉,以提高系统性能。(jit优化见后续文章)
        锁消除案例:StringBuffer的append方法是加了synchronized关键字修饰的,但是如果你是在一个方法里对一个StringBuffer类型的局部变量进行append操作,则不会有加锁/解锁的操作。

       什么是锁粗化?锁粗化就是多段代码,连续加锁/解锁操作,可能会被合并为一次加锁/解锁操作。虽然我们要求同步的代码段越少越好,但是加锁/解锁也会存在一定的性能消耗。

        锁粗化案例:

        //优化前
            for (int i = 0; i < 10000; i++) {
                synchronized (this) {
                    //do something
                }
            }
        //优化后:
            synchronized (this) {
                for (int i = 0; i < 10000; i++) {
                    //do something
                }
            }

3.锁的实现原理  

        堆内存用于存放创建的各种对象,对象中有一个head头部区域,主要用于存放Mark Word、Class Metadata Address及其他信息。其中Mark Word占4个字节,由30位的Bitfields和2位的Tag组成,Tag就是该对象的状态标志位,有4个取值,分别表示对象的四种状态:未锁定  --  轻量级锁 -- 重量级锁 -- 可GC回收标识。

 

内存结构
Mark Word对应的五种状态
BitfieldsTagState备注
HashcodeAge001Unlocked未锁定
Lock record address00Light-weight locked轻量级锁
Monitor address10Heavy-weight locked重量级锁
Forwarding address,etc.11Marked for GC可回收标志
ThreadIDAge101Biased/biasable偏向锁标志

           默认情况下JVM加锁过程会经历:未锁定(偏向锁)-->轻量级锁-->重量级锁,三个状态。下面一一分析。

锁升级过程

        默认情况下开启了偏向锁功能(即Bitfields的最后一位是1),如果有线程(线程-1)进行加锁操作,则会在ThreadID中记录当前获取到锁资源的线程的ID,然后该线程就可以进入同步代码段执行了,其实本质上还没有加锁,执行完同步代码段后的解锁操作也不会删除ThreadID信息。下次如果还是该线程再次需要获取这个锁资源,则直接比较ThreadID信息,无需加锁/解锁操作,提高了系统性能。偏向锁在Java 6和Java 7里是默认启用的,但是它在应用程序启动几秒钟之后才激活,如有必要可以使用JVM参数来关闭延迟-XX:BiasedLockingStartupDelay = 0。如果你确定自己应用程序里所有的锁通常情况下处于竞争状态,可以通过JVM参数-XX:-UseBiasedLocking=false关闭偏向锁的功能。

        如果有另外一个线程(线程-2)需要获取该锁资源,通过比较ThreadID,发现有其他线程(线程-1)占用了偏向锁,JVM会将锁升级为轻量级锁,Tag被修改为00,Lock record address被修改为当前占有该锁资源的线程(线程-1)的线程栈锁地址。

线程栈开辟Lock record记录占有的轻量级锁对象的Mark word信息

        线程栈会开辟一个空间(Lock record),存储当前锁定对象的MarkWord信息。Lock record可以存储多个锁定的对象信息。解锁过程是一个逆向恢复Mark word的过程,即将线程栈中Lock record记录的对象信息恢复到相应对象的Mark word中,然后将Lock record中的记录删除。另外一个线程(线程-2)获取锁资源是通过CAS机制修改对象的Mark word信息,修改成功则加锁成功,否则循环尝试。

        如果通过CAS机制多次尝试加锁依然失败后,就会进入重量级锁的状态(Tag=10),线程进入Monitor监视器的等待队列中,直到其他线程释放锁资源,等待集合中的所有线程再同时争抢,成功则成为_owner,失败则再次进入_EntryList集合等待。在Monitor中,记录了当前获得锁资源的线程(_owner),如果这个线程执行解锁操作则退出Monitor,如果执行了wait操作则进入等待notify集合(_WaitSet),直到收到其他线程发出的notify指令后再次成为owner。

重量级锁加锁过程

4.总结

        本文主要讲了锁的基本概念,synchronized关键字及其特性,以及java中锁的实现原理、锁升级的流程、monitor的工作原理。


        最后再说两句,笔者刚开始写技术笔记,理解不到位的地方还请多多包含,欢迎大家留言指正,谢谢大家。

 

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值