Synchronized锁升级 ——
偏向锁的撤销
偏向锁状态变化与最终升级为轻量级锁:
- A线程获取偏向锁成功,已经退出执行不再是活跃线程;B线程来获取偏向锁,默认前20次直接升级为轻量级锁
- A线程获取偏向锁成功,已经退出执行不再是活跃线程;B线程来获取偏向锁,默认20次以后,对象锁偏向线程B。达到40次阈值后,若再有其他线程(比如线程C)过来争抢,则触发批量撤销,该对象不再有任何偏向锁的情况。
批量重偏向:当一个对象,如果在短时间内(默认25s)在经过默认20次争抢的情况下,会将后面的所有争抢重新偏向争抢者线程。
代码示例:
public class SyncLockFlag1 {
static Thread A; static Thread B; static int loopFlag = 20;
public static void main(String[] args) { try { Thread.sleep(5000); } catch (InterruptedException e) { throw new RuntimeException(e); }
final List<Object> list = new ArrayList<>(); A = new Thread(() -> { for (int i = 0; i < loopFlag; i++) { Object obj = new Object(); list.add(obj); System.out.println("A加锁前第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); synchronized (obj) { System.out.println("A加锁中第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); } System.out.println("A加锁后第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); }
LockSupport.unpark(B); }, "Thread-A");
B = new Thread(() -> { LockSupport.park(); for (int i = 0; i < loopFlag; i++) { Object obj = list.get(i); System.out.println("B加锁前第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); synchronized (obj) { System.out.println("B加锁中第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); } System.out.println("B加锁后第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); }
System.out.println("新产生的对象:" + ClassLayout.parseInstance(new Object()).toPrintable()); }, "Thread-B");
A.start(); B.start(); } }
|
当B线程争抢第16次(不同的机器不同)的时候,触发了批量重偏向的阈值,在第16次及之后的争抢中,JVM会将对象的锁偏向线程B,因为JVM认为这个对象更适合线程B。
批量撤销:
代码示例
public class SyncLockFlag2 {
static Thread A; static Thread B; static Thread C; static int loopFlag = 40;
public static void main(String[] args) { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); }
final List<Object> list = new ArrayList<>(); A = new Thread(() -> { for (int i = 0; i < loopFlag; i++) { Object obj = new Object(); list.add(obj); System.out.println("A加锁前第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); synchronized (obj) { System.out.println("A加锁中第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); } System.out.println("A加锁后第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); }
LockSupport.unpark(B); }, "Thread-A");
B = new Thread(() -> { LockSupport.park(); for (int i = 0; i < loopFlag; i++) { Object obj = list.get(i); System.out.println("B加锁前第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); synchronized (obj) { System.out.println("B加锁中第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); } System.out.println("B加锁后第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); } LockSupport.unpark(C);
System.out.println("B 新产生的对象:" + ClassLayout.parseInstance(new Object()).toPrintable()); }, "Thread-B");
C = new Thread(() -> { LockSupport.park(); for (int i = 0; i < loopFlag; i++) { Object obj = list.get(i); System.out.println("C加锁前第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); synchronized (obj) { System.out.println("C加锁中第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); } System.out.println("C加锁后第" + (i + 1) + "次:" + ClassLayout.parseInstance(obj).toPrintable()); } System.out.println("C 新产生的对象:" + ClassLayout.parseInstance(new Object()).toPrintable()); }, "Thread-C");
A.start(); B.start(); C.start(); } }
|
如果基于批量重偏向的基础上,还在继续争抢达到40次,并且有第三条线程C加入争抢,此时会触发批量撤销。JVM会标记改对象不能使用偏向锁,以后新创建的示例对象直接以轻量级锁开始,此时才是真正完成了锁升级。
真正的锁升级,是依赖于对象的Class字节码的,而不是依赖于该对象Class的某一个实例对象。
资料:https://www.bilibili.com/video/BV1Je411f7PF/ 强推 河北王校长