Cloudy terrace, monthly land, thousands of lock-ups (I. Fairness and unfairness)

Is it surprising to see the title of the article? Why does a technical subject engage in such a literary and artistic topic? The title says that the lock is thousands of times heavy. Is it very image? Aren't there many kinds of locks in our development?

Lock

Since we have said that locks weigh thousands, how many kinds of locks are there, and how can they be classified? Why are they so classified? Let me explain to you.

Why lock?

Many times during interviews, people ask why they lock up. What is the effect of locking?

In fact, there will be concurrency in our development process. For example, when two people click on a button almost at the same time, it can be simply understood as concurrency. So who is the first and who is the second? Errors are likely to occur in the program. When resources are shared, concurrency will be involved. At this time, we may use locks to lock a resource. After I use them, you can move. That's why locks are used.

Classification of Locks

1. Fair Lock/Unfair Lock

2. Re-entrainable locks

3. Exclusive/Shared Locks
4. Mutex/Read/Write Locks
5. Optimistic Lock/Pessimistic Lock
6. Segmentation lock
7. Deviation Lock/Lightweight Lock/Heavy Lock
8. Spin lock

 

For the first time, let's talk about fair locks and unfair locks. After that, I will continue to analyze it in the article in the preface.

What is equity? What is unfair? Understanding in our daily life is not equal is fair, is not equal is unfair? It's almost the same.

Fair locks refer to multiple threads acquiring locks in the order in which they apply for locks.

Unfair locks refer to that the order in which multiple threads acquire locks is not in the order in which they apply for locks. It is possible that threads that apply for locks later will acquire locks more preferentially than threads that apply for locks first. It may cause priority reversal or hunger.

 

What are fair locks and unfair locks in JAVA code?

One is to use Java keyword synchronized to lock the corresponding classes or methods and code blocks.

The other is ReentrantLock, which can only be an unfair lock, while the latter is a lock that defaults to be unfair but achieves fairness.

The class diagram above looks uncomfortable, because there is really no good map for the lock ReentrantLock. We can draw it ourselves and understand it.

Let's look at the unfair lock first. Let me draw a picture for you to understand. Like public toilets, don't be disgusted with nausea, but it's absolutely easy to understand.

In the picture above, the administrator is Lock, and now A comes and says I'm going to the toilet. When the administrator sees that the toilet is empty, then you go in and A goes in.

At this time, there is A in the WC, which is in the progressive mode. At this time, B comes and B wants to go to the toilet.

But at this time there is A in the WC, and then the administrator Lock looks at it, there is someone inside, queue up....

Then the B had to hold back and go into the queue to queue up. Then there came another C. At that time A was still inside, and C could only queue up.

That's what it looks like.

At this time, a little later, a D came and wanted to go to WC. At this time, A just came to an end.

At this time, the unfair lock came on, Lock Administrator looked, there was no one inside, D you go in, this is the case.

And then because A came out and said, "I'm over," and B was about to go in when D came in, that's the unfair lock.

The call of unfair lock is that A is locked by Lock - > B goes to wait - > C waits behind B - > A ends, unlock - > unfair lock compareAndSetState(0, acquires), and nobody inside - > D is locked by Lock at this time A releases the lock. - -> The result is a step late, not in, can only continue to wait.

This is a simple understanding of unfair locks, the truth is quite simple, and fair locks are different, Lock administrator will tell the new D, you have several in front of the queue, you want to go in, queue behind.

This is how fair locks are invoked.

The front is the same, but when D comes, it's different.

When D comes - > lock knows - > Call hasQueued Predecessors () directly to tell D that someone is in the queue, and you go to the back to queue, so as to ensure fairness.

Next, let's look at the source code implementation of ReentrantLock and explain it.

public class ReentrantLock implements Lock, java.io.Serializable {        /*Synchronizer provides all implementation mechanisms */        private final Sync sync;       /**        Synchronization control basis for this lock. It will be converted to the following fair and unfair versions. Use the AQS state to indicate the number of lock holds.        */       abstract static class Sync extends AbstractQueuedSynchronizer {           private static final long serialVersionUID = -5179523762034025860L;           /**            Execute {@link Lock # lock}. The main reason for subclassing x is to allow fast paths to unfair versions.            */           abstract void lock();           /**            * Execute unfair tryLock. tryAcquire is implemented in subclasses, but both require unfair attempts of tryf methods.                Note that unfair locks are executed here, that is, when compareAndSerState is judged,                New threads may preempt the use of locks on queued threads            */           final boolean nonfairTryAcquire(int acquires) {               final Thread current = Thread.currentThread();               int c = getState();               //The state is 0, indicating that no thread currently holds a lock.               if (c == 0) {                   if (compareAndSetState(0, acquires)) {                       setExclusiveOwnerThread(current);                       return true;                   }               }               else if (current == getExclusiveOwnerThread()) {                   int nextc = c + acquires;                   if (nextc < 0) // overflow                       throw new Error("Maximum lock count exceeded");                   setState(nextc);                   return true;               }               return false;           }           protected final boolean tryRelease(int releases) {               int c = getState() - releases;               if (Thread.currentThread() != getExclusiveOwnerThread())                   throw new IllegalMonitorStateException();               boolean free = false;               if (c == 0) {                   free = true;                   setExclusiveOwnerThread(null);               }               setState(c);               return free;           }           protected final boolean isHeldExclusively() {               // While we must in general read state before owner,               // we don't need to do so to check if current thread is owner               return getExclusiveOwnerThread() == Thread.currentThread();           }           final ConditionObject newCondition() {               return new ConditionObject();           }           // Methods relayed from outer class           final Thread getOwner() {               return getState() == 0 ? null : getExclusiveOwnerThread();           }           final int getHoldCount() {               return isHeldExclusively() ? getState() : 0;           }           final boolean isLocked() {               return getState() != 0;           }           /**            Reconstruct an instance from a stream (that is, deserialize it).            */           private void readObject(java.io.ObjectInputStream s)               throws java.io.IOException, ClassNotFoundException {               s.defaultReadObject();               setState(0); // reset to unlocked state           }       }}}

 

The source code above is all about unfair locks. Let's see how fair locks are defined in the source code.

 

    /**        Synchronize objects for fair locking     */    static final class FairSync extends Sync {        private static final long serialVersionUID = -3000897897090466540L;        final void lock() {            acquire(1);        }        /**         tryAcquire A fair version. Access is not granted unless there is a recursive call or no waiter or the first one.            Here's a hasQueued Predecessors approach that ensures that we use locks sequentially, whether new or already queued.         */        protected final boolean tryAcquire(int acquires) {            final Thread current = Thread.currentThread();            int c = getState();            if (c == 0) {                if (!hasQueuedPredecessors() &&                    compareAndSetState(0, acquires)) {                    setExclusiveOwnerThread(current);                    return true;                }            }            else if (current == getExclusiveOwnerThread()) {                int nextc = c + acquires;                if (nextc < 0)                    throw new Error("Maximum lock count exceeded");                setState(nextc);                return true;            }            return false;        }    }

After explaining, let's make a summary.

1. Fair Lock: Old threads queue to use locks, while new threads still queue to use locks. 2. Unfair Locks: Old threads queue using locks; however, new threads are most likely to preempt locks already queued.

Later, I will update some articles about locks. I hope you can give us some advice and discuss progress together. Thank you.


Java Geek Technologies Public Number is set up by a group of technicians who love Java development, focusing on sharing original, high-quality Java articles. If you think our articles are good, please help us appreciate, read and forward support, and encourage us to share better articles.

Pay attention to the public number, you can reply to the "blog park" in the background of the public number, and get the author's Java knowledge system / interview materials for free.

Keywords: PHP Java

Added by otuatail on Wed, 24 Jul 2019 02:42:33 +0300