Java 线程知识笔记 (六) 锁与锁的状态升级

高速PCB设计中串扰的成因、影响与实战控制策略 在高速数字电路与硬件系统设计中,信号完整性是确保系统稳定可靠的核心基础。其核心原理在于,当信号在传输线上传播时,会伴随产生电场与磁场,若相邻导线距离过近,便会通过电磁场耦合产生不期望的能量转移,即串扰。这一现象会直接导致信号波形畸变、时序偏移和噪声裕量降低,严重影响系统性能。从技术价值看,深入理解并有效控制串扰,是提升数据传输速率、保障系统稳定性和降低误码率的关键。其应用场景广泛覆盖了从高速SerDes链路、DDR内存接口到高精度模拟电路等几乎所有现代电子系统。本文聚焦于高速PCB设计中的串扰问题,系统剖析 阅读详情

前言

上一篇【Java 线程知识笔记 (五) synchronized 用法与原理分析】主要分析了synchronized的用法和原理,由于synchronized加锁后会导致所有其他线程无法访问必须进行等待,尤其是在JDK1.6以前,可以说synchronized是一个非常重量级的锁。为了提高synchronized的效率,Java官方对synchronized进行了一个优化,本篇就主要说说synchronized的优化与锁的升级。更多线程知识内容请点击【Java 多线程和锁知识笔记系列】

Java 1.6 的优化

Java 1.6以后官方针对锁的优化,主要是增加了两种新的锁:偏向锁和轻量级锁。再加上本身重量级锁,那么锁基本上可以大致分为这三种,它们之间的区别主要是体现在等待时间上面。重量级锁比较暴力,一旦某个资源被一个线程加了锁,那么其他线程就不要再有任何想法了,必须等待其释放资源才可以。如果时间较长,就会造成程序卡顿,甚至崩溃掉,为了改进加了两个新锁。这两种新锁的概念是如何优化的呢,就需要弄清楚对象和monitor的关系。

对象头结构

任何一个实例对象都有一些共同的信息即对象头、实例变量、填充数据。其中对象头就是加锁的基础,其中存储的就是加锁的信息;实例变量就是存的属性变量的信息,私有公有之类的;填充数据则是数据的起始地址。我们要说的还是对象头,下面是对象头结构:

长度内容描述
32/64 bitMark Word存储对象的hashCode或者锁信息等
32/64 bitClass Metadata Address存储到对象类型数据的指针
32/64 bitArray Length数组的长度,如果当前对象是数组

从对象头的信息来看,Mark Word就是存放锁信息的地方,一共有32位用来存放锁信息,信息格式如下:

在这里插入图片描述

锁的状态

首先先解释下上表中锁状态的含义:

无锁状态:没有加锁,属于乐观锁,锁标记01。

偏向锁:在对象第一次被某一线程占有的时候,会检查偏向锁标记,然后把偏向锁标记置1,表示线程被占用使用的是偏向锁,锁标记置为01。接着写入线程ID,表示当前对象被哪个ID的线程占有了。当此线程占有期间,其他的线程访问的时候,就会有线程竞争。如果竞争成功,使用资源;如果竞争失败则等待下次竞争,偏向锁是轻量级锁,属于乐观锁。所谓的偏向锁,会比较倾向于首次占有的线程,也就是说两个线程竞争,第一次占有的线程会有比较大的概率分配到资源,这也就是偏向锁名称的由来:会偏向于首先占有该资源的线程。那么当前线程一旦具有优先权,其他线程很可能长久无法获取资源,为了改进这样一个状态,很多时候会采用大名鼎鼎非阻塞算法Compare and Swap(CAS)算法。正是因为这种特性,偏向锁更加适合用于竞争不激烈的时候。由于偏向锁会不断地和无锁状态进行切换,因此两者使用时间也是接近的。

轻量级锁:一般在线程有交替时使用。当偏向锁算法CAS竞争失败,就会修改偏向锁标记为00,升级为轻量级锁,属于乐观锁。

重量级锁:强互斥,锁住资源后不允许其他线程竞争,其他线程要想访问必须等待资源释放,等待时间长。当某资源已经处于轻量级锁的时候,会有别的线程前来竞争,继续竞争失败,则会修改标记为10,变为重量级锁,属于悲观锁。

自旋锁与锁消除

锁的升级也是要消耗时间的,尤其是不同等级之间的转换消耗的时间更多。而轻量级锁转换成重量级锁则是用户线程和核心线程(操作系统本身的线程)之间的转换非常耗时,所以我们要尽量减少这种转换,所以有了这样一种概念:当竞争失败的时候,先不着急进行转换,而是等待一会儿,由于线程执行的非常快,可能还没有升级到重量级锁的时候,资源就已经被释放了。正是基于这样一种逻辑,就有了自旋锁这一概念。

自旋锁:当竞争失败的时候,不是马上转化级别,而是执行几次空循环,如果执行空循环的时候,占有线程释放了资源,那么就不需要进行锁的升级,直接进行占用线程执行就好了因此自旋锁只是一种概念,并不是真正的锁。

锁消除: Java的即时编译编译器(JIT)在编译的时候把不必要的锁去掉。比如下面代码,用来给变量a赋值,就完全没有必要加锁,因此这个synchronized在编译的时候会被消去。

synchronized(this){
    int a=1;
}

锁的升级

除了上面所说,还要明白一个概念,锁永远都会加载对象(或者说堆中的资源上)上而不是线程上。所以升级过程为如下的步骤:

假设:线程A访问对象资源A则此时立刻对资源A加上偏向锁保证线程A访问资源A,且线程ID会被置为线程A。如果线程B来竞争,此时偏向锁不会主动释放,线程B可以看到该锁并知晓资源A已经被线程A使用,于是检查此时资源A是否被线程A使用(即线程A是否还存活),那么:

  1. 如果线程A已经销毁,则资源A分配给线程B,即分配偏向锁给线程B并把线程ID置为线程B。
  2. 如果线程A没有销毁,则检查线程A的操作栈,检查资源A的使用情况:
    a). 如果线程A仍然在使用资源A,则把资源A升级为轻量级锁,保证线程A的使用;
    b). 如果资源A已经不再使用,已经被线程A释放了,则资源A分配给线程B,后续同1。
  3. 资源A已经由于竞争,升级为轻量级锁(此时资源A依然是被线程A使用),线程B仍然需要竞争资源A。 因为轻量级锁会被认为竞争程度低,所以线程B被允许尝试竞争,当线程B再次发现资源A被轻量级锁锁住时,线程B会进入自旋状态或者说自旋锁(即空转一定时间,比如空的循环等等,等待资源A被释放)。此时认为资源A会很快被释放,所以线程B进入空转等待资源A。
  4. 但是当自旋超过一定次数,或者线程C在线程B自旋时,也来请求资源A,则资源A的轻量级锁被升级为重量级锁。由于重量级锁能够使得除了拥有当前资源的线程以外的线程都阻塞,等待资源的释放。所以此时资源A和线程A被重量级锁绑在一起,不会释放给线程B、线程C,因此线程B和线程C必须等待线程A释放资源A以后在进行新的竞争。

说完这些,可能大家会有个疑问:为什么要进行锁的升级?假如很多线程都是处于轻量级上,都处于自旋状态,那么其中正在运行一个线程以及释放锁了,但是此时有很多线程在自旋,都在相互竞争。可能造成所有的线程都会一直自旋下去,CPU空转。为了阻止CPU空转,所以用重量级锁把除了正在运行的线程以外的所有线程阻塞,保证不会有新的竞争。但是要注意的是锁是由低到高的升级,而且只会升级,而不会降级。因为即便是重量级锁一旦占有线程释放了,就直接是无锁状态,不存在一层一层降级的过程。

锁的重入性

当有些时候必须对某个资源进行二次加锁也是可以的,比如下面这段代码做了多重加锁。这样也是可以的,之前的博客中说过Java中每一个对象都有一个monitor对象,也就是一个监视器,加锁就是通过这个监视器去加锁。当某一个线程要占有这个对象的时候,先去检查monitor对象的计数器是不是0,如果是0表示没有线程占有这个对象,则成功占有这个对象,并且对这个对象的monitor计数器+1。如果不为0,则说明这个对象已经被别的线程占用,那么就等待。当线程释放某个对象的占有时monitor的计数器-1。注意这里是减一不是置0,也就是说同一线程可以对同一对象进行多次加锁,进行不断地+1、+1,这个就是线程的重入性。

public synchronized void methodName3(){
    synchronized (this){
        // code ......
        synchronized (this){		//加锁的对象不同也可以,但是要避免对象交叉造成的死锁
            // code ......
            synchronized (this){
                // code ......
            }
        }
    }
}

悲观锁与乐观锁

悲观锁:一般指写操作比较多,包括增删改,读操作(查)比较少的锁。也指不允许竞争的锁,遇到必须等待。
乐观锁:一般指读(查)操作比较多,但是写操作比较少的锁。也指可以竞争的锁,可以尝试竞争。
因此很多时候悲观锁一般要加锁,乐观锁一般只用版本控制,读取到最新的数据即可。只是一个概念,并不特地指某种所。

公平锁与非公平锁

公平锁:其实就是排队,先来先得FIFO的逻辑,因此每个锁都是公平的。
非公平锁:就是采用一定的算法或者优先度去对线程拿到锁的顺序进行调整,因此每个锁并不是公平获取资源的。
这两个锁各有优缺点,使用谁要看具体需求,比如紧急刹车作为一个功能来说,就必须用非公平锁。

死锁

当由加锁导致A线程等B线程释放资源,B线程等A线程释放资源,结果线程A和线程B都无法获取资源导致,程序卡死在这里的情况就是死锁。

总结

本篇延续着synchronized关键字的部分内容,对锁的概念、锁的原理以及锁的升级,进行了一个剖析,希望能够对各位理解Java中的锁有所帮助。下一篇【Java 线程知识笔记 (七) volatile实现机制】将会对volatile这个关键字的原理和使用做一个讲解。

第四章节 Qt字符串QString详解 QString 的使用方式和 C++标准库的 std::string 字符串的非常相似。 阅读详情

相关推荐

一文介绍Linux EAS

EAS只在系统负载不重时,即系统中每个CPU的利用率都低于其算力的80%时才被启用,而且选出的最节能CPU只有比线程当前运行的CPU节约能量大于6%时,线程才会迁移到该CPU运行。因此EAS为线程选择最节约能量的CPU来运行的前提条件是很苛刻的,针对重载场景(比如游戏),EAS的功能应该很少被使用起来,针对重载场景的功耗优化,这里可能是一个值得尝试的点。算力(capacity)是CPU调度中的一个基础概念,它反映的是一个CPU的计算能力,是个规格化的值,可以通过读取Android手机的文件节点。

youzhangjing_的博客 1234

Java锁降级与升级的策略探讨

在实际应用中,需要根据具体场景和需求选择合适的降级与升级策略,并注意实施过程中的注意事项。Java锁的主要目标是提供高效的并发控制,减少数据竞争和冲突,提高系统的吞吐量和响应速度。降级策略的核心思想是灵活调整锁的使用策略,以适应不同的应用场景和需求。通过升级策略,可以提高系统的灵活性和扩展性,更好地适应不断变化的市场和用户需求。(1)使用分布式锁:通过使用分布式锁,可以在多个节点间实现锁的分散管理,降低对单一锁的需求。(3)引入新技术:随着技术的不断发展,也可以引入新的锁机制和技术来满足更高层次的需求。

searchboy2025的博客 88

从零开始构建AURIX TC397开发环境:硬件选型与IDE配置全攻略

本文详细介绍了如何从零开始构建AURIX TC397开发环境,涵盖硬件选型、IDE配置及外设驱动开发等关键步骤。针对TriCore架构的高性能特性,提供了开发板选型建议、AURIX Development Studio(ADS)安装配置指南,以及GPIO和CAN FD驱动的实战代码示例,帮助开发者快速上手TC397系列微控制器的开发工作。

7up55的博客 205

Java中的锁:类型,比较,升级与降级

在Java中,锁的升级与降级是指在使用过程中对锁的变更。具体来说,锁升级是指将一个低级别的锁升级为高级别的锁,而锁降级是指将一个高级别的锁降级为低级别的锁。Java提供了多种类型的锁,包括内置的synchronized关键字、ReentrantLock、ReadWriteLock等。本文将详细介绍这些锁的类型,比较它们的性能和适用场景,并探讨锁的升级与降级。它提供了一种更加灵活的锁控制方式,与synchronized关键字相比,ReentrantLock具有更多的功能和配置选项。

qq_39508627的博客 435

PID参数自整定实战解析:从继电反馈到智能调节的完整实现

本文深入解析PID参数自整定的实战应用,从继电反馈原理到智能调节算法的完整实现。通过详细讲解振荡波形特征提取、参数计算的工程化改进及工业场景调试技巧,帮助工程师快速掌握PID自整定技术,提升控制系统性能。特别针对嵌入式系统优化和噪声环境下的稳定策略提供了实用解决方案。

weixin_28731223的博客 461

CESM笔记——CESM1.0-CESM1.2.2-CESM2.1.3三个版本模式工作流对比

最近测试了CESM1.0,CESM1.2.2和CESM2.1.3三个版本,不同版本在workflow上有一些区别,来做点笔记。 CESM1.0 测试compset:B_2000,F_2000 workflow: 1. create_newcase -case ~/CESM/cesm1_0/script/B_2000 -compset B_2000 -res f19_g16 -mach tianhe 2. cd B_2000 (optional) env_mach_pes.xml ##修改核数,

砍柴姑娘Jourosy的博客 4944

java锁升级 锁降级_synchronized的锁升级、锁膨胀

阅读文本大概需要3分钟。0x01:偏向锁偏向第一个拿到锁的线程。即第一个拿到锁的线程,锁会在对象头 Mark Word 中通过 CAS记录该线程 ID,该线程以后每次拿锁时都不需要进行 CAS(指轻量级锁)。如果该线程正在执行同步代码块时有其他线程在竞争(指其他线程尝试 CAS 让 Mark Word 设置自己的线程 ID),会被升级为轻量级锁。如果其他线程发现 Mark Word 里记的不是自...

weixin_35922444的博客 1025

17track单号查询逆向分析

这就说明该网站很有可能存在其它检测点,而检测本地替换最大可能的就是检测了代码格式化或者代码长度。也一样,要么导致网站卡死,要么会导致网页加载异常,这里可能就难到不少人了,让我们一步一步解决。而这个站主要是检测了代码格式化,无限循环检测造成了网站卡死,经多次测试发现检测点共三处。方法中代码很长,但是经调试后发现,它其实只返回一个固定的值,这里直接写死即可。同样的,虽然看起来很长,但是跟过之后就会发现流程很简单,这里主要就是对。追加了一些值,是一条很长的语句,整理代码。,核心流程也很简单,稍微跟一下就出来了,

pyzzd的博客 1623

Java并发(十三):读写锁之间的锁降级和锁升级

回顾锁降级锁降级的必要性锁升级 回顾 之前已经分析了读写锁之间是如何进行加锁的,下面就看看,读写锁之间怎么进行锁降级与锁升级的 锁降级 读写锁的锁降级指的是写锁降级成为了读锁 当一个线程获取了写锁,并且又获取了读锁(获取写锁的线程可以获取读锁),那么当该线程释放了写锁时,该线程拥有的锁就会进行降级,变为了读锁,其实这个实现从之前看加锁源码就知道了,读锁和写锁的获取都是分开的,所以写锁的释放不会影响到读锁的持有 锁降级的必要性 锁降级的情景是,拿了写锁,然后再拿读锁,这有什么必要性吗?拿了写锁我就进行修改了.

GDUT_Trim的博客 2324

ReAct框架到多智能体协作:OpenClaw任务分解机制的演进与突破

AI的最终价值是落地,是帮用户解决实际问题,而不是停留在聊天界面里。从ReAct框架到多智能体协作,OpenClaw的任务分解体系走了一条非常务实的路线:所有的优化都是为了解决真实场景里的痛点,所有的设计都是为了让用户用起来更省心。很多人说现在的AI还是“人工智障”,那是因为他们没有用到真正能落地的AI产品。当你用过OpenClaw,体验过你说一句话它就能把一整套复杂任务全都做完的感觉,你就会明白:AI代替人做重复性工作的时代,已经真的来了。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 311

【AI】在RK3576上,使用RKNN实现MeloTTS(文本转语音)

在瑞芯微的RK3576上实现TTS(文本转语音)功能,使用CPU实现时,几乎把CPU资源全部耗尽,没有实用价值。通过将MeloTTS转为onnx,再转为RKNN,来使用RK3576的NPU硬件加速,成功实现TTS。

郭老二 564

说一下synchronized的锁的升级和降级

锁的升级和降级过程是 JVM 为了在不同的竞争情况下提高性能而自动进行的调整。在实际开发中,开发者一般不需要直接干预这个过程,但了解这个机制可以帮助我们更好地理解 Java 中多线程同步的性能特点和优化方向。同时,在高并发场景下,合理地使用同步机制和避免过度竞争是提高程序性能的关键。关键字使用的锁有状态的升级和降级过程,主要涉及偏向锁、轻量级锁和重量级锁。

challenge51all的专栏 1395

2020计算机专业保研夏令营面经:中科院计算所网数机试题目

计算所网络数据科学与技术重点实验室的第一阶段是机试。机试不限程序语言,要求提交源代码。

乔卿的博客 2957

Java中的锁升级

关于锁升级的四种级别,即如何升级

热爱编程的菜鸟 9820

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

java锁升级过程

java中对象锁有4种状态:(级别从低到高) 1.无锁状态 2.偏向锁状态 3.轻量级锁状态 4.重量级锁状态 对象头分两部分信息,第一部分用于存储哈希码、GC分代年龄等,这部分数据被称为"Mark Word"。在32位的HotSpot虚拟机中对象未被锁定的状态下,Mark Word的32bit空间中的25bit用于存储对象哈希码,4bit用于存储对象分代年龄,2bit用于存储锁标志位,1bit固...

天高任鸟飞,海阔凭鱼跃 1万+

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

多线程锁的升级原理是什么?

多线程锁的升级原理是什么? 锁的级别从低到高: 无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁 锁分级别原因: 没有优化以前,sychronized是重量级锁(悲观锁),使用 wait 和 notify、notifyAll 来切换线程状态非常消耗系统资源;线程的挂起和唤醒间隔很短暂,这样很浪费资源,影响性能。所以 JVM 对 sychronized 关键字进...

ConstXiong 2万+

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

锁升级及线程池相关

在JVM底层实现锁的过程中,有三类锁:偏斜锁、轻量级锁、重量级锁在Java6之前,synchronized的实现完全依靠重量级锁(系统内的互斥锁),从用户态转为内核态非常消耗资源。偏斜锁、轻量级锁、重量级锁。所谓的升级、降级,就是JVM优化synchronized运行的机制,当JVM检测到不同的竞争状况时,会自动切换到适合的锁实现,这种切换就是锁的升级、降级。偏向锁只有单线程执行情况下,该线程在后续访问时,便会自动获取锁。

fada_da_da的博客 1013

Java多线程之锁的升级

Java多线程之锁的升级 先说说为什么会有锁升级 因为Sycronized是重量级锁(也是悲观锁),每次在要进行锁的请求的时候,如果当前资源被其他线程占有要将当前的线程阻塞加入到阻塞队列,然后清空当前线程的缓存,等到锁释放的时候再通过notify或者notifyAll唤醒当前的线程,并让其处于就绪状态。这样线程的来回切换是非常消耗系统资源的,而且有的时候,线程刚挂起资源就释放了。而Java的线...

always_younger的博客 5638

Java锁升级

前言:对象的对象头中的存放着synchronized用的锁信息,详细结构内容请看《Java对象的内存布局》 锁的升级 Java SE1.6为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁”和“轻量级锁”,所以在Java SE1.6里锁一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,它会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向...

潘建南的博客 3137

Java中锁的升级与死锁详解

锁类型锁的状态适用场景特点偏向锁初始锁状态没有其他线程竞争时几乎没有开销轻量级锁偏向锁竞争失败时存在轻微的锁竞争时使用 CAS 尝试获取锁重量级锁轻量级锁竞争失败时存在大量线程竞争时会导致线程阻塞与上下文切换死锁是指两个或多个线程因争夺资源而互相等待,导致程序无法继续执行。互斥条件:至少有一个资源必须处于非共享模式,即只能由一个线程使用。请求与保持条件:线程在请求资源时,已经持有其他资源。不剥夺条件:已经分配给线程的资源,在没有使用完之前,不能强行剥夺。循环等待条件。

一碗黄焖鸡三碗米饭的博客 1334

Java锁和锁的升级

本文介绍了Java中锁的基本知识和锁升级,包括无锁状态、偏向锁、轻量级锁和重量级锁。锁的升级过程根据竞争情况和线程的状态逐渐提升锁的级别。在多线程编程中,需要根据实际情况选择合适的锁类型,并注意锁的使用对程序性能的影响。公众号请关注"果酱桑", 一起学习,一起进步!

果酱 の 博客 4043

如何实现Java多线程环境下的锁升级机制

在Java多线程环境中,锁升级机制是JVM优化性能的核心策略,通过动态调整锁粒度(无锁→偏向锁→轻量级锁→重量级锁)来平衡线程安全与性能开销。

iddlw75046的博客 778

java多线程:6、多线程中锁的升级原理是什么?什么是自旋锁?

文章目录多线程中锁的升级原理是什么? 多线程中锁的升级原理是什么?

weixin_42924812的博客 2634

JAVA:多线程和锁+锁升级机制

特性wait()park()所属类Object是否需要锁必须(块内)不需要唤醒方式notify()中断响应抛不抛异常,需手动检测锁竞争唤醒后需重新竞争锁唤醒后直接执行,无锁竞争典型应用传统同步机制协作并发工具类底层实现。

weixin_45149504的博客 1035

java锁升级

我们简称为。

weixin_40482816的博客 4482

Java线程与锁知识点总结

文章目录Java线程与锁知识点总结进程与线程线程的状态实现线程的几种方式进程和线程的区别Java进程和线程的关系Thread中的start和run方法的区别Thread和Runnable的关系如何实现处理线程的返回值volatile和synchronized的区别sychronized和Locksynchronized和ReentrantLock的区别sleep和wait的区别notify和not...

qq_24095055的博客 3172
上一篇: Java 线程知识笔记 (五) synchronized 用法与原理分析
下一篇: Java 多线程和锁知识笔记系列
Smallc0de
博客等级 码龄15年 148粉丝 147原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值