一个想休息的线程:JVM究竟是怎么处理锁的?怎么不让我阻塞呢?

我是一个线程,生活在JVM(Java虚拟机)中, 这一段日子过得有些无聊,整个世界彷佛只有这一我的,每天忙着执行代码,想休息一下都很难。程序员

 

我据说人类写的代码中有些特殊的地方,叫作临界区,好比synchronized修饰的方法或者代码块,他们很是神奇,在同一时刻JVM老大只容许一个线程进入执行。编程

 

实际上,老大设置了一把锁,抢到了这把锁就能够执行,不然只能阻塞,等待别人释放锁。数据结构

 

老大说,阻塞就是不用干活了,老老实实地等着就行。多线程

 

居然还有这等美事! 赶忙让我阻塞一次吧。并发

 

但是老大又说:“每次设置锁我都得和操做系统打交道,请他在内核中维护一个什么Mutex(互斥量)的东西,他还得把大家这些线程阻塞,切换,这但是一笔巨大的费用啊,因此这些锁仍是少用为妙。”函数

 

我运气也很差,我不知道执行了多少代码,调用了多少函数,居然一次也没遇到临界区!操作系统

 

我想也许这个程序员编程时不当心,没有考虑多线程并发的状况; 也有多是这些程序大部分都是无状态的,多少个线程执行都没有问题。线程

 

因而我只好一直执行下去, 不知道过了多少天,我激动地发现,一个synchronized修饰的代码块终于出现了:3d

 

Account account = ...对象

synchronized(account){

    ...临界区的代码...

}

 

偏向锁

 

我满心指望别的线程已经进入了代码块,那我就能够阻塞、休息。

 

即便没有其余线程进入临界区,老大为我申请锁, 也得和操做系统协商什么互斥量,从用户态进入核心态,再从核心态返回用户态,总要花些功夫吧。

 

但是老大根本没有去找操做系统, 只是看了看这个account对象的所谓“对象头”,其中有个叫作Mark Word的东西,彷佛是个什么数据结构, 里边有几个标识位,还有其余数据。

 

 

老大随手使用CAS操做把个人线程ID记录到了这个Mark Word当中,修改了标识位,而后告诉我说: 能够了,你如今拥有这把锁了,进去执行代码吧。

 

 

我惊奇地说:“老大你不和操做系统协商设置Mutex了?”

 

老大说:“不用了,你看如今就你一个线程进入了这个代码块,我只要记录下你的线程ID,就表示你拥有这把锁了,不用操做系统介入。”

 

我得到了锁,开始执行被synchronized包裹的代码块。

 

等到我第二次执行到这个synchronized的时候,老大只是看了一眼锁对象account的Mark Word就说:“你的线程ID还在,还持有着这个对象的锁,进入临界区执行吧。”

 

我连喘口气的机会都没有,只好继续执行。

 

老大说,这叫偏向锁,在没有别的线程竞争的时候,一直偏向我,可让我一直执行下去。

 

我是多么期盼来一个新的线程来和我竞争啊!

 

轻量级锁

 

很快,机会就来了。

 

另一个线程0x3704也要进入这个代码块执行,可是锁对象account 保存的是个人线程ID,他是无法进入临界区的。

 

我心想,咱们两个至少得有一个进入阻塞状态,休息一下子了。

 

可是老大仍是不去操做系统协商,只是说: 我把这个偏向锁升级一下,变成一个轻量级的锁吧。

 

老大把锁对象account恢复成无锁状态,在咱们俩的栈帧中各自分配了一个空间,叫作Lock Record, 把锁对象account的Mark Word在咱们俩这里各自复制了一份,叫作Displaced Mark Word, 这名字真奇怪。

 

而后把个人Lock Record的地址使用CAS放到了Mark Word当中,而且把锁标志位改成00, 这其实就意味着我也已经得到了这个轻量级的锁了,能够继续进入临界区执行。

 

 

0x3704没有得到锁,但仍是不阻塞,老大让他自旋几回,等待一下子。

 

等到我退出临界区,释放锁的时候,须要把这个Displaced markd word 使用CAS复制回去。接下来他就能够加锁了。

 

咱们两个交替着进入临界区,执行这段代码,相安无事,不多出现真正的竞争。

 

即便是出现了竞争,想得到锁的线程只要自旋几回,等待一下子,锁就可能释放了。

 

很明显,若是没有竞争或者轻度的竞争,轻量级锁仅仅使用CAS操做和Lock record就避免了重量级互斥锁的开销,对JVM老大来讲,确实是个好主意。

 

重量级锁

 

轻量级锁运行得挺好,我仍是没有机会休息,终于有这么一天,0x3704 正在持有锁,在临界区辛苦地执行代码。 我自旋了好屡次,0x3704仍是没释放锁。 这时候JVM老大说: 自旋次数太多了,太浪费CPU了,接下来升级为重量级锁!

 

这个重量级锁须要操做系统的帮忙,依赖操做系统底层的Mutex Lock。

 

只见老大建立了一个monitor 对象, 把这个对象的地址更新到了Mark word当中。

 

锁升级了!

 

 

因为0x3704还在持有锁运行,而我终于进入了求之不得的状态:阻塞!  终于能够休息一下了!

 

仔细一想,老大煞费心机地设置了偏向锁和轻量级锁,就是为了不阻塞,避免操做系统的介入, 这两种锁无非就是针对这两种状况:

 

偏向锁: 一般只有一个线程在临界区执行

 

轻量级锁: 能够有多个线程交替进入临界区,在竞争不激烈的时候,稍微自旋等待一下就能得到锁。

 

至于重量级锁,也是我最为期待的锁,那就是出现了激烈的竞争,只好让咱们去阻塞休息了。

相关文章
相关标签/搜索