Writing in the front
I believe that those of you who are new to Java or don’t have a deep understanding of Java multithreading must be as frustrated as ME in the process of learning in the back end of Java. It doesn’t matter, you can continue to read, After reading this article, I believe you will have a deeper understanding of the concepts of heavyweight lock, spin lock, lightweight lock, biased lock, pessimistic lock, optimistic lock, etc.
In this article, I will tell you what these locks are, where they come from, how they relate to each other, and what the differences are. (Knock on the blackboard ~~, here’s the point)
Locking mechanisms and various locks
Why lock it?
Multithreading students must know that ** lock this thing, so why on earth procedures to lock it? To sum up in one sentence: because of the multi-threading situation between threads of resources competition. That is, if the program were to run single-threaded throughout, there would be no locking and no need for it, because there would be no contention, but reality is often cruel, and in practice that is not the case.
We take an example to illustrate the insecurity of multithreaded situations: you borrow a book in the library haruki murakami’s sitting on the seat the melancholy grocery store quiet looking at, suddenly your roommate called off the lay counselor, not in the bedroom will be please to and tea, so you can only in the page you are looking at the Angle of the books. Then he put it back on the shelf. When you come back from the real world and get this book and open it, oh my God!! What’s going on? A lot of pages are dog-eared? Didn’t I remember that the page I was reading was folded?
That’s right, because while you’re away you can’t guarantee that other students won’t read the book, that other students won’t make marks in the book.
How to solve it? Very simple! Take the book back and return it when you’re done. This way people won’t mess with your mark, than people want to see it only after you return. You take it back, you look at it, and you return it. It’s a lock-execution-lock-release process. Nice, that solves the problem of resource insecurity in multiple threads.
Whoa, whoa, whoa, whoa, whoa, whoa, whoa, whoa, whoa! Enter the theme
Heavyweight lock
We know that to enter a synchronous, thread-safe method, we need to acquire the method’s lock first, and when we exit the method, the lock is released. If we can’t get the lock, it means that another thread is executing the method, and we immediately go into the blocked state, wait for the thread that holds the lock to release the lock, and then wake up from the blocked state to acquire the method lock.
This type of lock that blocks immediately before it is acquired is called a heavyweight lock.
spinlocks
As we know, the process of a thread from running state to blocking state is very time-consuming, because it not only needs to save the execution state and context data of the thread at this time, but also involves the conversion from user state to kernel state. Of course, waking up a thread from a blocked state is also very time consuming.
I just said that if a thread can’t get the lock, it will immediately block. In reality, though it can’t get the lock at that moment, another thread can release the lock in 0.0001 seconds. If it had been 0.0001 second slower to pick up the lock, it would have been fine without the time-consuming blocking/waking process.
However, the heavyweight lock is such a pit, it is not willing to wait, as soon as not to enter the blocking state. To solve this problem, we introduced another type of lock that was willing to wait a certain amount of time – the spin lock.
A spin lock is one that, if you can’t get the lock at this point, instead of immediately blocking, it waits a while to see if someone else releases the lock in the meantime. How can I wait? This is similar to a thread that is there to empty the loop, and if it does not get the lock a certain number of times, then it will enter the blocking state.
As for the cycle of waiting for several times, this can be artificially specified a number.
Adaptive spin lock
For the spin lock, each thread waits the same number of times. For example, if I set it to 100, the thread will block after 100 empty cycles.
An adaptive spin lock is awesome because it doesn’t require us to manually specify how many times it should loop, it determines how many times it should loop, and each thread can loop differently. The main reason for doing this is that we think that if a thread has held the lock recently, or if it has held the lock often before, then we think it has a very high chance of getting the lock again, so it will loop more times.
And if some thread never gets the lock, or if it rarely gets it, then we think it’s less likely to get it again, so we let it loop less. Because it’s CPU intensive when you short the loop there.
So this kind of spin lock that can adjust the number of cycles based on the thread’s most recently acquired lock state is called adaptive spin lock.
Lightweight lock
The three types of locks described above — heavyweight, spin, and adaptive spin locks — all have one feature: when entering a method, a lock is added, and when exiting a method, the lock is released.
The reason for the lock is that they are afraid that someone will sneak in while the method is executing, so they have to lock it to prevent other threads from entering. It’s the equivalent of locking the door every time you leave your room and unlocking it when you come back.
If there are no threads to compete with them for locks, aren’t they locking for nothing? You know, lock this process is the operating system to help this big guy, is very time consuming,. Lightweight locks have emerged to address the overhead of locking too often.
Lightweight lock think, when you perform in the inside of the method, is rarely just someone also to execute this method, so that when we enter a way there is no lock, we only need to do a tag is ok, that is to say, we can use a variable to record the method at this time whether anyone in the execution. That is, if the method is not being executed, when we enter the method, we use the CAS mechanism to mark the state of the method as being executed, and when we exit the method, we change the state to being executed.
Obviously, the operation that uses CAS to change the state is much less expensive than the lock operation.
Whereas you might say, the idea that there’s no competition, that’s what you’re saying, but what if there’s competition? That is, when a thread executes a method, someone else is already executing the method.
If there was competition, we would decide that the lightweight lock was no longer suitable, and we would upgrade the lightweight lock to the heavyweight lock.
So lightweight locks are good for situations where it’s rare for multiple threads to compete for a lock, that is, where multiple threads always stagger the time to acquire the lock.
Biased locking
We already think that lightweight locks are light enough. However, biased locks are much easier. Biased locks think that every time you enter a method, you need to change the state with CAS, and exit also needs to change.
Biased lock, in fact, for a method, is rarely two threads to execute, to do, in fact, only one thread in the execution of this method, equivalent to a single thread, is actually a single thread, that there is no need to add a lock.
But after all, the actual situation of multithreading, single thread is just its own idea, so, bias lock into a method is handled like this: If no one has entered the method before, the first time a thread enters the method, the CAS mechanism is used to mark the method as being executed, which is similar to lightweight locking and locking. The ID of the thread is also recorded, which is equivalent to recording which thread is executing.
And then, when this thread exits the method, it doesn’t change the state of the method, it just exits, lazily, because it thinks that no other thread is going to execute the method except its own thread.
Then, when the thread wants to enter the method again, it checks the state of the method. If the method is marked as being executed and the thread ID is its own, it simply enters the method and does nothing
You see, how convenient, the first time to enter the CAS mechanism to set, later in and out of nothing to do, directly enter and exit.
However, reality is always cruel, after all, the real situation is multithreading, so what if another thread enters the method? If this happens, at least two threads will need to execute the methodology, which means biased locking is no longer applicable and will be upgraded from biased locking to lightweight locking.
So biased locking works when there’s only one thread executing a method at all times.
Pessimistic locks and optimistic locks
The first three types of locks, heavyweight, spin and adaptive spin locks, must be added before entering the method, which we call pessimistic locks. The pessimistic lock always thinks that if you don’t lock beforehand, something will happen. This kind of thinking is actually a little pessimistic, which is probably the source of the pessimistic lock.
Optimistic locks, on the other hand, believe that it is ok not to lock, we can not lock, if there is a conflict, we are trying to solve, for example, CAS mechanism, the above mentioned lightweight locks, is optimistic locks. It will not lock immediately, but wait for a real conflict, trying to solve it.
conclusion
Here is also roughly finished, a brief introduction of the popularity of the point we should understand their origin, principle. Each type of lock has its own application and advantages and disadvantages.
Things to keep in mind:
- The lock level goes from low to high: no lock -> bias lock -> Lightweight lock -> Heavyweight lock
- Locks can only be upgraded, not degraded. As the scene changes, locks can be unlocked -> biased -> lightweight -> heavyweight locks, but cannot be degraded.