锁及锁优化

        Java中通过锁来实现线程安全问题。Java的线程是映射到原生的操作系统上的,如果要阻塞或唤醒一个线程,都需要操作系统来帮忙完成。这就需要从用户态转换到核心态,因此状态转换需要耗费很多的处理器时间。所以Synchronized锁是Java语言中一个重量级操作。所以我们引进了各种锁优化。

1,自旋锁与适应性自旋

        挂起线程和恢复线程都需要转入内核态中完成,这些操作给系统的并发性能带来了很大的压力。如果能有一个以上的处理器能让两个或两个以上的线程同时并行执行。我们可以让后面请求锁的那个线程稍等一下,看看持有锁的线程是否很快就会释放锁。为了让线程等待,我们只需要让一个线程执行一个循环(自旋)。这就是所谓的自旋锁。

缺点:如果锁占用时间很长,自旋的线程就会白白消耗处理器资源。

适应性自旋:自旋的时间不再固定,是由前一个在同一个锁上的自旋时间及锁的拥有者的状态来决定的。如果在 同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也有可能再次成功,进而它将允许自旋等待持续更长的时间。

2,轻量级锁

在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。

我们来看一下HotSpot虚拟机的对象头。

Java的对象头通常由两个部分组成,一个是Mark Word存储对象的hashCode或者锁信息,另一个是Class Metadata Address用于存储对象类型数据的指针,如果对象是数组,还会有一个部分存储的是数据的长度。

MarkWord :实现轻量级锁、偏向锁的关键。

在代码进入同步块的时候,如果此同步对象没有被锁定(锁标志位为“01”),虚拟机首先将在当前线程的栈帧中建立一个名为锁机录(Lock Record)的空间,用于存储锁对象目前的Mark Word拷贝(Displaced Mark Word)。

如果有两条以上的线程争用同一把锁,那轻量级锁就不再有效,要膨胀为重量级锁。

然后当前线程的Lock Record的地址使用CAS放到了Mark Word当中,并且把锁标志位改为00, 这意味着当前线程也已经获得了这个轻量级的锁了,可以继续进入临界区执行。

解锁过程也是通过CAS操作进行的,如果对象的Mark Word 仍然指向着线程的锁记录,那就用CAS操作把对象当前的Mark Word和线程中复制的Displaced Mark Word 替换回来,如果替换成功,整个同步过程就完成了。如果替换失败,说明有其他线程尝试过获取该锁,那就要在释放的同时唤醒被挂起的线程。

3,偏向锁

目的是消除数据在无竞争的情况下的同步原语,进一步提高程序的运行性能。偏向锁会偏向于第一个获取它的线程,如果在接下来的执行过程中,该锁没有被其他线程获取,则持有偏向锁的线程将永远不需要再进行同步。

偏向锁运行过程:锁对象第一次被线程获取的时候,虚拟机会将对象头的标志位设为“01”,既偏向模式。同时使用CAS操作把这个锁的        线程ID记录到对象的Mark Word中,如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不         再进行任何操作。当有另外一个线程去尝试获得这把锁时,偏向模式就宣告结束。根据锁对象目前是否处于被锁定的状态,撤销偏         向后恢复到未锁定或轻量级锁的状态。

猜你喜欢

转载自blog.csdn.net/qq_37937537/article/details/82856597