Introduction to the

A friend of mine recently consulted me. Some time ago, he was asked by an interviewer whether synchronized is a fair lock or **** is an unfair lock. At that time on the circle, the final interview results can be imagined, today we use a popular case with code to explain the fair lock and unfair lock. In fact, the concept of fair lock is only available in JUC toolkit. For example, ReentrantLock only has the concept of fair lock. In this article, we combine the examples in life with two codes to illustrate ReentrantLock fair lock and unfair lock, and prove that **synchronized is unfair lock. ** hope to be helpful to small friends.

The concept of fair lock and unfair lock

  • Fair lock: Take a simple example, there are five students have to queue up for lunch every day, for the sake of simplicity, we define a number for each of these five students, respectively numbered 001 to 005, the five students in line according to the first come, lunch, the students who come first can get lunch first. Each student is a thread, in this process later students are not allowed to jump the queue, this is fair lock ****.
  • ** not fair lock: later to the students not necessarily after the dozen to the meal, in the process of the dozen meal, is allowed to jump the queue, this thread insert behavior people think is unfair. For example, 001, 002, 003, 004 came first and queued first. 005 came last and should have queued after 004, but 005 saw that 001 just finished his meal and left, so he jumped the queue. That is, the order of cooking changes from 001->002->003->004->005 to 001->005->002->003->004. Actually you should understand by now, fair lock is normal queue, not fair is jump the queue. Of course you might wonder, right? Is 005 inserted into the back of 001 will be successful, the answer is not necessarily, it depends on the opportunity, we just said “005 see 001 just finished the meal to leave”, the following should be 002, may be the rice aunt did not ask 002 to eat what, 005 has been in front of the row, then 005 jumped the queue successfully, this is the opportunity. Let’s use program code to deepen our understanding.

Synchronized Indicates an unfair lock

/** * @author: jiaolian * @date: Created in 2020-12-31 16:01 * @description: Created in 2020-12-31 16:01 Private static class DiningRoom {// private static class DiningRoom {// public void getFood() { System.out.println(thread.currentThread ().getName()+": queued "); Synchronized (this) {System. Out. Println (Thread. The currentThread (). The getName () + ": @ @ @ @ @ @ rice in @ @ @ @ @ @ @"); } } } public static void main(String[] args) { DiningRoom diningRoom = new DiningRoom(); For (int I =0; i<5; i++) { new Thread(()->{ diningRoom.getFood(); }," student id :00"+(I +1)).start(); }}}Copy the code

Code like this: We define an internal class DiningRoom to represent the canteen. In the getFood method, synchronized lock is used to modify this to the DiningRoom instance object (DiningRoom object in line 22). In the main class, five students numbered from 001 to 005 are allowed to eat at the same time. Is it used to test whether the students who queue up first can get the meal first? Run the program and get the execution result of **** as shown in the picture below. Students 002->004->001->003->005 go to queue first, but the order of eating is 002->003->001->004->005, indicating that two students 003 and 001 cut in front of 004. We analyzed the execution process in detail. 002 grabbed the lock first and released the lock, which should have been followed by 004 grabbed the lock and went to eat (because 004 was in the queue before 003), but 003 grabbed the lock, grabbed the lock and released the lock, which was the first time to jump the queue. Now we still come to 004 to grab the lock, but failed to grab it, 001 grabbed it again, release the lock, 004 grabbed it, this is the second time to jump the queue, then 004->005 grabbed the lock, release the lock, the program is finished. Since 003 and 001 cut the queue, we code to prove that synchronized is unfair. Next we look at ****ReentrantLock fair and unfair locks.

ReentrantLock An 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: Created in 2020-12-31 11:11 */ public class UnFairLockTest {private static final Lock Lock = new ReentrantLock(false); Private static class DiningRoom {// getFood public void getFood() {try { System.out.println(thread.currentThread ().getName()+": queued "); LOCK.lock(); System. The out. Println (Thread. CurrentThread (). The getName () + ": @ @ @ @ @ @ rice in @ @ @ @ @ @ @"); } catch (Exception e) { e.printStackTrace(); } finally { LOCK.unlock(); } } } public static void main(String[] args) throws InterruptedException { DiningRoom diningRoom = new DiningRoom(); For (int I =0; i<5; i++) { new Thread(()->{ diningRoom.getFood(); }," student id :00"+(I +1)).start(); }}}Copy the code

In line 13 we define Lock Lock = new ReentrantLock(false); The ReentrantLock argument 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 placed ina try, finally block. The purpose is that if something happens to the code after a try is locked and the LOCK is finally executed, the LOCK is always released. In the main class, let five students from 001 to 005 go to eat at the same time, and get one of the execution results as shown in the figure below. Students from 001->004->005->003->002 go to queue first, but the order of eating is 001->005->004->003->002. Then 004 should have grabbed the lock, because it queued first, but 005 grabbed the lock before 004, cooked, 005 than 004 later, but cooked first, this is unfair lock, the following execution results according to the first come first served execution, the end of the program. We use code to prove that ReentrantLock is an unfair lock. Next, let’s look at another case of ReentrantLock as a fair lock.

Already fair lock

Private static final Lock Lock = new ReentrantLock(false); private static final Lock Lock = new ReentrantLock(false); ** Private static final Lock Lock = new ReentrantLock(true); ** no matter how many times the implementation can draw a conclusion: the first line of children’s shoes can be the first meal, not allowed to jump the queue reflects is fair lock.

Underlying principles of ReentrantLock

Already, which is based on AbstractQueuedSynchronizer (abstract queue synchronizer, aqs), aqs underlying maintains a leading the two-way linked list, used to synchronize threads, list each Node in the Node, each Node will record the thread information Node from top to bottom, Aqs controls the life cycle of a Node. As shown in the figure below, AQS also contains conditional queues. Locks and conditional queues are one-to-many, that is, a lock can correspond to multiple conditional queues. Communication between threads is controlled in conditional queues through await, single/singleAll methods. Synchronized has only one conditional queue that uses wait, notify, and notifyAll. Producers and consumers of multithreaded communication wait/notify and condition/await/signal conditional queues and Synchronized Usage Principles and Lock Optimization escalation Processes (Interviews) are examples of clear and simple examples. Conditional queues also exist as linked lists. Lock is based on juc package implementation, synchronized is a native method based on c++ implementation.

conclusion

Today with the example of life into the code, a detailed introduction to the fair lock and non-fair lock, and a simple introduction to the principle of AQS implementation, to your advice is to seriously knock the code again, if the implementation of the code should be able to see it, like please click like and pay attention to oh. My name is Lian [public number], while calling while practicing.