批量
synchronized中的偏向锁是将线程id标记到markword中以占用锁
1.撤销
当对象调用hashcode时,会把hashcode存到markdown,这个hashcode是延迟更新的,只有调用的时候才会填入,所以这个时候如果有偏向锁,hashcode会将偏向锁挤走,也就是末尾bit置为001正常状态。
当线程1被标记了偏向后,线程2也调用了锁,两个线程之间不存在竞争关系,则会发生偏向撤销,也就是锁会升级成轻量级锁,状态从101->000->001.
2.批量重偏向。
当频繁出现撤销时(初始阈值为20),jvm会认为该类的锁是否确实标量了线程1.从而会运行其重偏量到其他线程,状态也从001->101,即线程2的偏向状态。
实现步骤:
修改线程2的锁列表范围0-40,前0-19现象为撤销101->000->001,后20-40的现象为重偏向101->101->101
之后所有从线程1修改太到其他线程的偏向锁都会重新偏向。
3.批量撤销。
当出现频繁撤销时(阈值为40),jvm会认为该类确实不应该偏向线程1,则会将该类的所有对象全部不可偏向,包括新生成的锁对象。
实验步骤:
修改线程2的锁列表范围0-40,其中0-19现象为撤销101->000->001,后20-40的现象为重偏向101->101->101,线程3的锁列表范围为20-50,其中20-40的锁偏向为从线程2的修改偏向,会计为偏向撤销101->000->001,升级到轻量级锁,40-50则会触发批量撤销条件自动取消偏向状态也为撤销101->000->001。
所以,40触发条件的撤销为从线程1转为线程2的20个撤销+线程2转为线程3的20个撤销。
代碼:
package com.lizhenxin.java0422.b3; import com.lizhenxin.java0422.util.Sleeper; import lombok.extern.slf4j.Slf4j; import org.openjdk.jol.info.ClassLayout; import java.util.ArrayList; import java.util.List; import java.util.Vector; /** * @author lizhenxin * @create 2022-04-24-18:05 */ @Slf4j public class Test4 { public static void main(String[] args) throws Exception { // Sleeper.sleep(1); int forCount = 100; Vector<Dog> list = new Vector<Dog>(); Thread t1 = new Thread(() -> { for (int i = 0; i < forCount; i++) { Dog d = new Dog(); list.add(d); synchronized (d) { log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } } synchronized (list) { list.notify(); } }, "t1"); t1.start(); Thread t2 = new Thread(() -> { synchronized (list) { try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("===============> "); for (int i = 0; i < 40; i++) { Dog d = list.get(i); log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); synchronized (d) { log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } synchronized (MyClassLayout.class) { MyClassLayout.class.notify(); } }, "t2"); t2.start(); Thread t3 = new Thread(() -> { synchronized (MyClassLayout.class) { try { MyClassLayout.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("===============> "); for (int i = 20; i < 50; i++) { Dog d = list.get(i); log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); synchronized (d) { log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } synchronized (lock.class) { lock.class.notify(); } }, "t3"); t3.start(); Thread t4 = new Thread(() -> { synchronized (lock.class) { try { lock.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("===============> "); for (int i = 20; i < forCount; i++) { Dog d = list.get(i); log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); synchronized (d) { log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } }, "t4"); t4.start(); t4.join(); log.debug(MyClassLayout.printMarkDown(new Dog())); } } class Dog{ } class MyClassLayout{ static String printMarkDown(Object instance){ String[] split = ClassLayout.parseInstance(instance).toPrintable().split("\\r\\n"); String s = split[2]; return s.substring(60,s.length()); } } class lock{ }
maven:
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.9</version> </dependency>