The biased lock theory is too abstract. In practice, understand how biased locks occur and how to upgrade [Actual Combat]

Get into the habit of writing together! This is the third day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event

lock upgrade

  • Above we mainly introduce what are bias locks, lightweight locks, and heavyweight locks. And analyze the differences and usage scenarios of the three. Remember the integer-set upgrade operation in the Redis chapter. In locks, we also design lock promotion and demotion. Above we also introduced biased locks when there is no competition, and lightweight locks when there is competition.
  • But lightweight locks are cas operations and spin waits. Spinning is only suitable for situations with less concurrency. If there are many concurrent threads, it may take a long time to acquire the lock, and the overhead during the spin is also huge, so it is necessary to upgrade the lightweight lock. So when is it time to upgrade heavyweight locks? The number of spins is also set in the JVM, and if it exceeds a certain number of times, it will be upgraded to a heavyweight lock.

Bias lock upgrade lightweight lock

  • Personally, I think the focus is still on the process of lock escalation. Because the biased lock will not be revoked actively, the lock escalation process involves scenarios such as batch lock revocation and batch lock bias.

image-20211213152554303.png

  • Remember the storage structure of the biased lock in the markword of the lock object, the last three digits are 101 to indicate the biased lock. About Lock Record is the pointer of the lock record object on the top of the thread stack mentioned above. About the lock record, the markword of the entire lock object is stored internally, and here we need to pay attention to EPOCH, which translates to the meaning of epoch. We simply understand that the version is good
  • Speaking of version numbers, we also have to be familiar with the JVM's two property settings for biased locks

image-20211213153045152.png

  • Biased lock revocation occurs when a lightweight lock escalation occurs. If the JVM finds that the number of lock revocations for a certain type of lock is greater than or equal -XX:BiasedLockIngBulkRebiasThreshold=20to, it will declare the biased lock invalid. To invalidate the bias lock is to add 1 to the version number, that is, EPOCH+1;
  • When the total number of revocations of a class lock is greater than or equal to -XX:BiasedLockingBulkRevokeThreshold=40, the subsequent locks will default to lightweight locks.
class Demo{
    String userName;
}
public class LockRevoke {
    public static void main(String[] args) throws InterruptedException {
        List<Demo> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add(new Demo());
        }
        final Thread t1 = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                for (int i = 0; i < 99; i++) {
                    Demo demo = list.get(i);
                    synchronized (demo) {
                    }
                }
                TimeUnit.SECONDS.sleep(100000);
            }
        });

        final Thread t2 = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                synchronized (list.get(99)) {
                    System.out.println("第100个对象上锁中,并持续使用该对象" + ClassLayout.parseInstance(list.get(99)).toPrintable());
                    TimeUnit.SECONDS.sleep(99999);
                }
            }
        });

        final Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    Demo demo = list.get(i);
                    synchronized (demo) {


                        if (i == 18) {
                            System.out.println("发送第19次锁升级,list.get(18)应该是轻量级锁" + ClassLayout.parseInstance(list.get(18)).toPrintable());
                        }
                        if (i == 19) {
                            System.out.println("发送第20次锁升级,会发生批量重偏向;纪元+1;后续偏向锁都会偏向当前线程;list.get(19)应该是轻量级锁" + ClassLayout.parseInstance(list.get(19)).toPrintable());
                            System.out.println("因为第100对象仍然在使用,需要修改起纪元" + ClassLayout.parseInstance(list.get(99)).toPrintable());
                        }
                        if (i == 29) {
                            System.out.println("在批量重偏向之后;因为第一次偏向锁已经失效了,所以这里不是轻量级而是偏向该线程的偏向锁" + ClassLayout.parseInstance(list.get(29)).toPrintable());
                        }
                        if (i == 39) {
                            System.out.println("发送第40次锁升级,发生批量锁撤销;这里应该是轻量级锁后续都是轻量级" + ClassLayout.parseInstance(list.get(39)).toPrintable());
                        }
                    }
                }

            }
        });
        
        t1.start();
        t2.start();
        TimeUnit.SECONDS.sleep(5);
        System.out.println("第一次上锁后list.get(0)应该偏向锁:" + ClassLayout.parseInstance(list.get(0)).toPrintable());
        System.out.println("第一次上锁后list.get(19)应该偏向锁:" + ClassLayout.parseInstance(list.get(19)).toPrintable());
        System.out.println("第一次上锁后list.get(29)应该偏向锁:" + ClassLayout.parseInstance(list.get(29)).toPrintable());
        System.out.println("第一次上锁后list.get(39)应该偏向锁:" + ClassLayout.parseInstance(list.get(39)).toPrintable());
        System.out.println("第一次上锁后list.get(99)应该偏向锁:" + ClassLayout.parseInstance(list.get(99)).toPrintable());
        t3.start();
       

    }

}
复制代码
  • The above is a typical case integration of biased lock-heavy bias and biased lock revocation.
  • First of all, our t1 thread is the first to lock the first 99 objects and release them immediately, because our vm settings are delayed to cancel the bias lock, please refer to the beginning of the article for how to set them.
  • The second thread t2 only locks the last object, the difference is that it is permanently occupied and not released after being locked. Then others will not be able to acquire the lock of the last object
  • The third thread starts to preempt resources with the object initialized above. The third thread only loops 40 times, because the default maximum number of revoke biased locks by the JVM is 40 times. There are lightweight locks in the back.
  • Because the third thread will be re-biased in batches, the subsequent revocation of the biased lock will not be caused. If you see batch lock revocation, you must open a thread to lock. So thread 4 will continue to cause cancellation, but it must be executed after thread 4, otherwise t3 and t4 will be executed at the same time, which will cause heavyweight locks, because one of the scenarios of heavyweight locks is: 1 bias lock, 1 lightweight lock, 1 is requesting will trigger heavyweight lock
  • When i==18, that is, the 19th element, is locked in the third thread, because the bias lock has been locked before, although the lock is released, the bias lock itself will not be released. . So at this time, the 19th element has the lock revocation first, and then the lightweight lock is applied. So here is a lightweight lock when predicting the 19th object
  • Then come to i==19, which is the 20th element, because the JVM default class total revocation greater than or equal to 20 will cause batch re-bias. What do you mean? Locking before i==19 in t3 is lightweight. After i==19, the lock will be biased when it is locked, but it is biased towards thread 3, not thread 1. Here we can compare with the first i==19 memory layout, except that the thread id is different, there is also a different epoch,

image-20211213161634774.png

  • Why would I start a separate thread to lock list.get(99) above? It is to test that when batch re-bias occurs, you can intuitively see that the lock epoch information in use has been modified, so as not to cause the lock to be discarded.

image-20211213162536276.png

  • We can see that when batch re-bias occurs, the lock epoch information in use will be updated. If it is not updated, the JVM will consider it to be an abandoned bias lock. Of course, after a batch rebias occurs, the object lock will not be revoked when the object lock is acquired again. Because the previous lock has been abandoned, let's get the subsequent lock information. Let's take a look at list.get(29).

image-20211213162904537.png

  • The fourth thread continues to cause revocation after the third thread, and when the total number of revocations reaches 40, the JVM will think that the subsequent locks of this type are not suitable for biased locks, and are directly lightweight locks

image-20211213164055601.png

Guess you like

Origin juejin.im/post/7087740752398139399