偏向锁
在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,因此为了减少同一线程获取锁(会涉及到一些CAS操作,耗时)的代价而引入偏向锁。
使用场景,一般是一个线程访问同步块,优点是加锁解锁不需要额外的 消耗
1、线程访问同步代码块 判断当前锁状态,锁标志位01,再判断是否是偏向锁 ,是否偏向锁为1 则是偏向锁;
2、是偏向锁,再判断对象头中Markwork记录的ThreadId是否是当前线程;
如果是,当前线程直接获取到偏向锁,继续执行同步块代码;
如果不是,则通过CAS操作,尝试将对象头中Markwork中的ThreadID更新为自己的线程id,来获取偏向锁 ,当CAS操作成功,线程拿到了偏向锁,则继续执行同步块代码;当CAS操作失败,开始对原线程持有的偏向锁撤销,当原持有偏向锁 的线程到达安全点后,停止原持有偏向锁的线程;
3、原持有偏向锁线程的活动状态,如果已经退出同步块代码,则唤起新的线程来竞争偏向锁,对象重新偏向;如果是原持有偏向锁线程没有退出同步块,则升级轻量级锁;
轻量级锁
由于出现锁的竞争,会将偏向锁升级为轻量级锁
1、升级轻量级锁 之前,线程会在线程栈中分配锁记录存储空间;
2、拷贝对象头中Markwork的锁信息封装为LockRecord到线程栈的锁记录中;
3、通过CAS操作,将线程栈中的分配锁记录的指针地址更新到对象头的Markwork中,如果更新成功,获取轻量级锁;
4、如果CAS操作失败,则利用自旋来尝试重新获取轻量级锁,自旋其实就是不断的循环进行CAS操作直到能成功替换,所以轻量级锁又叫自旋锁;
5、lockrecord的作用:在这里实现了锁重入,每当同一个线程多次获取同一个锁时,会在当前栈帧中放入一个lockrecord,但是重入是放入的lockrecord关于锁信息的内容为null,代表锁重入。当轻量级解锁时,每解锁一次则从栈帧中弹出一个lockrecord,直到为0;
6、当自旋到一定次数之后,还是拿不到锁,锁再次升级,升级为重量级锁。
重量级锁
在重量级锁中将LockRecord对象替换为了monitor对象的实现。主要通过monitorenter和monitorexit两个指令来实现。需要经过系统调用,在并发低的情况下效率会低。
几种锁的优缺点
总结
只有一个线程进入临界区 -------偏向锁
多个线程交替进入临界区--------轻量级锁
多个线程同时进入临界区-------重量级锁