Synchronized锁升级 ——
偏向锁的撤销(非必需)
示例代码1:
启动前配置JVM参数:-XX:BiasedLockingStartupDelay=0
public class BiasedLockRelease1 { static Thread A; static Thread B;
public static void main(String[] args) { final List<Object> list = new ArrayList<>(); A = new Thread(() -> { Object obj = new Object(); list.add(obj); System.out.println("A加锁前:" + ClassLayout.parseInstance(obj).toPrintable()); synchronized (obj) { System.out.println("A加锁中:" + ClassLayout.parseInstance(obj).toPrintable()); } System.out.println("A加锁后:" + ClassLayout.parseInstance(obj).toPrintable()); LockSupport.unpark(B); }, "Thread - A");
B = new Thread(() -> { LockSupport.park(); Object obj = list.get(0); System.out.println("B加锁前:" + ClassLayout.parseInstance(obj).toPrintable()); synchronized (obj) { System.out.println("B加锁中:" + ClassLayout.parseInstance(obj).toPrintable()); } System.out.println("B加锁后:" + ClassLayout.parseInstance(obj).toPrintable()); System.out.println("新产生的对象:" + ClassLayout.parseInstance(new Object()).toPrintable()); }, "Thread - B");
A.start(); B.start(); } }
|
运行结果:
锁只能升级,不能降级。
1)A线程获取偏向锁,并且A线程Terminated退出。B线程争抢偏向锁,会直接升级当前对象的锁为轻量级锁。(仅针对争抢了一次的情况)
示例代码2:
public class BiasedLockRelease2 { static Thread A; static Thread B;
public static void main(String[] args) throws InterruptedException { Object obj = new Object(); System.out.println("A加锁前:" + ClassLayout.parseInstance(obj).toPrintable()); A = new Thread(() -> { synchronized (obj) { System.out.println("A加锁中:" + ClassLayout.parseInstance(obj).toPrintable()); try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }, "Thread - A"); A.start();
Thread.sleep(500);
System.out.println("B加锁前:" + ClassLayout.parseInstance(obj).toPrintable()); B = new Thread(() -> { synchronized (obj) { System.out.println("B加锁中:" + ClassLayout.parseInstance(obj).toPrintable()); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }, "Thread - B"); B.start(); } }
|
2)A线程获取偏向锁,并且A线程尚未释放偏向锁(还处于同步代码块中),B线程此时来争抢偏向锁,此时会直接升级为重量级锁。
修改BiasedLockRelease2的代码:
A = new Thread(() -> { synchronized (obj) { System.out.println("A加锁中:" + ClassLayout.parseInstance(obj).toPrintable()); } try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } }, "Thread - A"); A.start();
|
3)A线程获取偏向锁,并且A线程释放了偏向锁,但是A线程还没有Terminated,此时B线程来争抢锁,此时会升级为轻量级锁。
=> 总结:(针对争抢一次的情况)
当尝试第一次争抢偏向锁时,如果A线程已经Terminated,升级为轻量级锁;如果A线程尚未Terminated,并且未释放锁,直接升级为重量级锁;如果A线程尚未Terminated,并且已经释放了锁,升级为轻量级锁。
示例代码3:
public class BiasedLockRelease3 { public static void main(String[] args) throws InterruptedException { List<Object> list = new ArrayList<>(); for (int i = 0; i < 20; i++) { Object obj = new Object(); list.add(obj); System.out.println("A加锁前:" + ClassLayout.parseInstance(obj).toPrintable()); Thread A = new Thread(() -> { synchronized (obj) { System.out.println("A加锁中:" + ClassLayout.parseInstance(obj).toPrintable()); try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); A.start(); } Thread.sleep(200);
for (int i = 0; i < 20; i++) { Object obj = list.get(i); System.out.println("B加锁前:" + ClassLayout.parseInstance(obj).toPrintable()); Thread B = new Thread(() -> { synchronized (obj) { System.out.println("B加锁中:" + ClassLayout.parseInstance(obj).toPrintable()); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); B.start(); }
Thread.sleep(5000); synchronized (list.get(19)) { System.out.println("再次加锁:" + ClassLayout.parseInstance(list.get(19)).toPrintable()); } System.out.println("新产生的对象:" + ClassLayout.parseInstance(new Object()).toPrintable()); } }
|
4)A线程获取偏向锁,并且A线程没有释放偏向锁(处于同步代码块中),B线程多次争抢锁,会在加锁过程中采用重量级锁;但是锁被释放之后
,当前对象还是会以轻量级锁的初始状态执行。
锁升级是在线程运行过程中和争抢过程中的一种升级。
5)A线程获取偏向锁,并且A线程释放了偏向锁,但是A线程还没有Terminated,此时B线程来多次争抢锁,部分争抢会升级为轻量级锁,部分争抢会保持偏向锁。
偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁的撤销,需要等待全局安全点(在这个时间点没有正在执行的安全码)。它会首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着;如果线程不处于活动状态,则将对象头设置成无锁状态;如果线程仍然活着,拥有偏向锁线程的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和对象头的Mark
Word要么重新偏向其他线程,要么恢复到无锁状态或者标记对象不适合作为偏向锁,最后唤醒暂停的线程。