This is the sixth day of my participation in the First Challenge 2022. For details: First Challenge 2022.

Batch rebias and batch undo

From the locking and unlocking of biased locking can be seen that in the process of repeatedly when only one thread enters the synchronized block, biased locking performance costs in basic can be ignored, but when there are other thread attempts to acquire the lock, will need to wait until the safe point, then biased locking cancellation for unlocked state or upgrade for lightweight lock, Therefore, in the case of frequent multi-thread competition, biased locking can not only improve performance, but also lead to performance degradation. Therefore, there are batch rebiased and batch undo mechanisms.

Each class maintains a bias lock undo counter that increases by 1 each time a bias lock is revoked on an object of that class. When this value reaches the bias threshold (20 by default), the JVM considers the bias lock of that class to be problematic. So there will be batch heavy bias.

Each class object corresponds to an EPOCH field. The Mark Word of each biased lock object also has this epoch field. Its initial value is the epoch value of the class in which the object was created. At the same time, the stack of all threads in the JVM is traversed to find all biased locks of the class being locked, and the epoch field of the class is updated to the new value. When the next lock is acquired, the epoch value of the object and the epoch value of the class are not equal, which is considered as biased to another thread. Instead of doing undo, CAS changes its Mark Word thread ID to the current thread ID

After the rebias threshold (20 by default) is reached, the JVM assumes that the class counter continues to grow. When it reaches the batch undo threshold (40 by default), the JVM considers the class to be in multithreaded contention and marks the current class as unbiased. Go straight to lightweight lock logic.

Application scenario (problem solved) :

1. Bulk rebias: When one thread creates a large number of objects and performs the initial synchronization, and another thread subsequently operates on those objects as locks, resulting in a large number of lock rebias operations.

Bulk Revoke: It is inappropriate to use partial lock in a scenario where multiple threads are obviously competing.

Java system default parameters

java -XX:+PrintFlagsFinal
Copy the code

We can get through-XX:BiasedLockingBulkRebiasThreshold-XX:BiasedLockingBulkRevokeThresholdTo manually set thresholds. Default values are as follows:

Batch bias lock weight bias

The experimental code is as follows:

public class A1 {
    public static void main(String[] args) throws Exception {
        // Delay generation can bias the object
        Thread.sleep(5000);

        // Create 100 bias locks for thread T1
        List<A> listA = new ArrayList<>();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                A a = new A();
                synchronized(a) { listA.add(a); }}try {
                To prevent JVM thread reuse, keep thread T1 alive after object creation
                Thread.sleep(100000000);
            } catch(InterruptedException e) { e.printStackTrace(); }}); t1.start();// Sleep 3s to ensure that thread T1 has completed the creation of the object
        Thread.sleep(3000);
        System.out.println("Print t1 thread, object header for 20th object in list:");
        System.out.println((ClassLayout.parseInstance(listA.get(19)).toPrintable()));

        // Create thread T2 to compete for the lock that has exited the synchronized block in thread T1
        Thread t2 = new Thread(() -> {
            // There are only 30 cycles in this loop!!
            for (int i = 0; i < 30; i++) {
                A a = listA.get(i);
                synchronized (a) {
                    // Print the result of the 19th and 20th deflection respectively
                    if (i == 18 || i == 19) {
                        System.out.println("The first" + (i + 1) + "Secondary bias result"); System.out.println((ClassLayout.parseInstance(a).toPrintable())); }}}try {
                Thread.sleep(10000000);
            } catch(InterruptedException e) { e.printStackTrace(); }}); t2.start(); Thread.sleep(3000);
        System.out.println(Print the object header for the 11th object in the list:);
        System.out.println((ClassLayout.parseInstance(listA.get(10)).toPrintable()));
        System.out.println(Print the object header for the 26th object in the list:);
        System.out.println((ClassLayout.parseInstance(listA.get(25)).toPrintable()));
        System.out.println(Print the object header for the 41st object in the list:);
        System.out.println((ClassLayout.parseInstance(listA.get(40)).toPrintable())); }}Copy the code

Let’s take a look at the result. The 20th shared lock object created in thread0 is currently biased lock, thread ID -594241531

Now let’s take a look at the T2 thread. The first 19 times all produced lightweight locks, and the threshold value of batch bias was 20 when it reached the 20th time. At this time, it was not a lightweight lock, but a biased lock, and the biased lock thread T2, thread ID: -619083515

Look again at the object header after the bias lock ends. The first 20 objects did not trigger the batch rebias mechanism, and thread T2 became lock-free after releasing the lock.

The batch re-bias mechanism is triggered by the 20-30 objects, which are in the biased lock state and biased towards thread T2, whose ID is -619083515

After the 31st object, no batch rebias mechanism is triggered, and the object is still biased towards thread T1, whose ID is -594241531 Note: It is recommended that the shared object be a custom class object

Batch bias lock undo

When the threshold for unlocking bias locks exceeds 40 times, the JVM decides that bias should not be allowed, and all objects of the entire class become unbiased, including newly created objects. Note: for statistical batch undo a parameter: – XX: BiasedLockingDecayTime wasn’t up to 40 times = 25000 ms scope, cancel the number 0, clock again

public class B1 {

    public static void main(String[] args) throws Exception {

        Thread.sleep(5000);
        List<A> listA = new ArrayList<>();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i <100 ; i++) {
                A a = new A();
                synchronized(a){ listA.add(a); }}try {
                Thread.sleep(100000000);
            } catch(InterruptedException e) { e.printStackTrace(); }}); t1.start(); Thread.sleep(3000);

        Thread t2 = new Thread(() -> {
            // Here we loop 40 times. The batch undo threshold was reached. Procedure
            for (int i = 0; i < 40; i++) {
                A a =listA.get(i);
                synchronized (a){
                }
            }
            try {
                Thread.sleep(10000000);
            } catch(InterruptedException e) { e.printStackTrace(); }}); t2.start(); Thread.sleep(3000);
        System.out.println(Print the object header for the 11th object in the list:);
        System.out.println((ClassLayout.parseInstance(listA.get(10)).toPrintable()));
        System.out.println(Print the object header for the 26th object in the list:);
        System.out.println((ClassLayout.parseInstance(listA.get(25)).toPrintable()));
        System.out.println(Print the object header for the 90th object in the list:);
        System.out.println((ClassLayout.parseInstance(listA.get(89)).toPrintable()));


        Thread t3 = new Thread(() -> {
            for (int i = 20; i < 40; i++) {
                A a =listA.get(i);
                synchronized (a){
                    if(i==20||i==22){
                        System.out.println("The first" thread3+ i + "Time"); System.out.println((ClassLayout.parseInstance(a).toPrintable())); }}}}); t3.start(); Thread.sleep(10000);
        System.out.println("Re-output new instance A");
        System.out.println((ClassLayout.parseInstance(newA()).toPrintable())); }}Copy the code

Looking at the output, this part is a little different from the batch bias above. Focus on thread ids

The first 20 objects did not trigger the batch rebias mechanism, and thread T2 became lock-free after releasing the lock.

The batch rebias mechanism is triggered by the 20-40 objects, which are in the biased lock state and biased towards thread T2, whose ID is -1005618939

After the 41st object, the batch rebias mechanism is not triggered, and the object is still biased towards thread T1, whose ID is -1039208443

T3 thread starts to compete for the lock, because the threshold for batch revocation has been reached, and the objects lista.get (20) and lista.get (22) have already been re-biased to the lock, so when T3 acquires the lock again, it will not trigger re-biased to t3.

At this point, a batch undo is triggered and the object collides into a lightweight lock

Finally, let’s look at the newly generated object. It was supposed to be biased, but after a batch rebias, and a batch undo, it went lock-free.

Bulk rebias and bulk undo summary

  • Batch rebias and batch undo are optimizations for classes, independent of objects.
  • Batch locks cannot be re-biased after being re-biased once.
  • When a class has triggered the batch undo mechanism, the JVM defaults to the fact that the current class has a serious problem, depriving the class of its newly generated objects of locking bias.

The resources

  • Docs.oracle.com/javase/tuto…
  • www.cnblogs.com/xidongyu/p/…
  • www.cnblogs.com/kkkkkk/p/55…
  • www.cnblogs.com/LemonFive/p…