The explanation of fair lock and unfair lock will make you unforgettable

Fair lock and unfair lock concept
Fair lock: Take a simple example, five students have to line up to eat dinner every day. For the sake of simplicity, we define a number for each of these five students, which are number 001 to number 005. The five classmates line up on a first-come-first-served basis, and the first classmates can get the meal first. Each student is a thread, and later students are not allowed to jump in the queue during this process. This is a fair lock.
Unfair lock: Afterwards, the classmates may not be able to catch the meal. During the meal, it is allowed to jump the queue. This thread insertion behavior is considered unfair. For example, for example, the students numbered 001, 002, 003, 004 queued up first, 005 came in the queue at the end and should have been behind 004, but 005 saw that 001 just left after dinner, so he jumped in. That is to say, the order of cooking rice changes from 001->002->003->004->005 to 001->005->002->003->004. In fact, you should understand now that fair lock means normal queuing, and unfair means skipping the queue. Of course you may have questions? Will 005 be inserted after 001 will succeed? The answer is not necessarily. It depends on the timing. We just said "005 looks at 001 and left right after finishing the meal", the following should be 002, maybe Auntie Dafan hasn't Ask 002 what to eat, just see if 005 has been queued to the front, then 005 has successfully jumped in the queue. This is the time. Below we use the program code to deepen our understanding.
synchronized unfair lock

/**
 * @author :jiaolian
 * @date :Created in 2020-12-31 16:01
 * @description:食堂打饭:synchronized不公平
 * @modified By:
 * 公众号:叫练
 */
public class SyncUnFairLockTest {
    
    

    //食堂
    private static class DiningRoom {
    
    
        //获取食物
        public void getFood() {
    
    
            System.out.println(Thread.currentThread().getName()+":排队中");
            synchronized (this) {
    
    
                System.out.println(Thread.currentThread().getName()+":@@@@@@打饭中@@@@@@@");
            }
        }
    }

    public static void main(String[] args) {
    
    
        DiningRoom diningRoom = new DiningRoom();
        //让5个同学去打饭
        for (int i=0; i<5; i++) {
    
    
            new Thread(()->{
    
    
                diningRoom.getFood();
            },"同学编号:00"+(i+1)).start();
        }
    }
}
复制代码

The above code: We define an internal class DiningRoom to represent the dining hall. In the getFood method, we use a synchronized lock to modify this to point to the DiningRoom instance object (the diningRoom object in line 22). In the main class, let five students numbered 001 to 005 go to dinner at the same time. Used to test whether the students who line up first can get the meal first? Run the program to get one of the execution results as shown in the figure below. 002->004->001->003->005 students go to the queue first, but the order of the meal is 002->003->001->004->005 , It means that the two classmates 003 and 001 have jumped into the queue and are inserted in front of 004. We analyze the execution process in detail. 002 first grabbed the lock for dinner and released the lock. It should be 004 that grabbed the lock for dinner. (Because 004 came in line before 003), but 003 grabbed the lock, fetched food, and released the lock. This is the first time to jump in the queue. Now it is still 004 to grab the lock, but it was robbed by 001 again. After the lock was released, it was grabbed by 004. This is the second time to jump in the queue, and then 004->005 to grab the lock, release the lock, and execute the program. complete. Because 003 and 001 jump in the queue, we use code to prove that synchronized is an unfair lock. Then we look at ReentrantLock fair lock and unfair lock.
Insert picture description here
ReentrantLock unfair lock

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author :jiaolian
 * @date :Created in 2020-12-31 11:11
 * @description:非公平锁测试  在获取锁的时候和再获取锁的顺序不一致;
 * @modified By:
 * 公众号:叫练
 */
public class UnFairLockTest {
    
    

    private static final Lock LOCK = new ReentrantLock(false);

    //食堂
    private static class DiningRoom {
    
    
        //获取食物
        public void getFood() {
    
    
            try {
    
    
                System.out.println(Thread.currentThread().getName()+":正在排队");
                LOCK.lock();
                System.out.println(Thread.currentThread().getName()+":@@@@@@打饭中@@@@@@@");
            } catch (Exception e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                LOCK.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
    
    
        DiningRoom diningRoom = new DiningRoom();
        //让5个同学去打饭
        for (int i=0; i<5; i++) {
    
    
            new Thread(()->{
    
    
                diningRoom.getFood();
            },"同学编号:00"+(i+1)).start();
        }
    }
}
复制代码

The above code: we defined Lock LOCK = new ReentrantLock(false) in line 13 of the code; the parameter of ReentrantLock is false to indicate an unfair lock. The above code needs to be locked with LOCK.lock() and unlocked with LOCK.unlock(). It needs to be put in the try, finally code block, the purpose is that if the code is abnormal after the lock is locked in the try, the lock is finally executed LOCK.unlock(), the lock can always be released. In the main class, let five students numbered 001 to 005 go to dinner at the same time, and get one of the execution results as shown in the figure below. 001->004->005->003->002 students go to the queue first, but the order of the dinner It is 001->005->004->003->002. Here, 005 has jumped in the queue and inserted in front of 004. We analyze the execution process in detail: 001 first grabbed the lock and released the lock. Next, it should have been 004 grabbing the lock because it queued first, but 005 grabbed the lock before 004, and 005. After 004, the meal is served first, which is an unfair lock. The subsequent execution results are executed first, and the program ends. We use code to prove that ReentrantLock is an unfair lock. Next, let's look at another situation of ReentrantLock as a fair lock.
Insert picture description here
ReentrantLock fair lock is
based on the above case, we will not repeat the code, change the private static final Lock LOCK = new ReentrantLock(false) in the 13 lines of the above code; the parameter is changed from false to true, private static final Lock LOCK = new ReentrantLock( true); no matter how many times it is executed, a conclusion can be drawn: the children's shoes in the queue can be served first, and the not allowed to jump in the queue reflects the fair lock. . The
Insert picture description here
underlying principle of ReentrantLock
ReentrantLock is implemented based on AbstractQueuedSynchronizer (abstract queue synchronizer, aqs for short). The bottom layer of aqs maintains a leading doubly linked list to synchronize threads. Each node of the linked list is represented by Node. Each Node records thread information, upper and lower nodes, For information such as node status, aqs controls the life cycle of Node. As shown in the figure below, aqs also contains conditional queues. Locks and conditional queues (condition) have a one-to-many relationship, which means that a lock can correspond to multiple conditional queues. The communication between threads is in the conditional queue through await, single/ SingleAll method control. Synchronized has only one condition queue to be implemented with wait, notify/notifyAll. I won’t expand on it here, "Example of hen laying eggs: multithreaded communication producer and consumer wait/notify and condition/await/signal condition queue" And "Synchronized usage principle and lock optimization and upgrade process (interview)" can read my article, there are a lot of clear and simple cases. The conditional queue also exists in the form of a linked list. Lock is implemented based on the juc package, and synchronized is a native method based on c++.
Insert picture description here
Reference: "2020 latest Java basics and detailed video tutorials and learning routes!

Original link: https://juejin.cn/post/6913524662160850951

Guess you like

Origin blog.csdn.net/weixin_46699878/article/details/112179002