[Multithreading Advanced] Common Locking Strategies


Column introduction: JavaEE from entry to advanced

Topic source: leetcode, Niuke, Jianzhi offer.

Creation goal: record the learning process of learning JavaEE

I hope to help others while improving myself, and make progress together with everyone and grow up with each other.

Academic qualifications represent the past, ability represents the present, and learning ability represents the future! 


Table of contents:

1. Common locking strategies

1.1 Optimistic lock vs pessimistic lock

1.2 Read-write lock:

1.3 Heavyweight locks vs lightweight locks

1.4 Spin Lock (Spin Lock)

1.5 Fair lock vs unfair lock

1.6 Reentrant lock vs non-reentrant lock

          1.7 Related Interview Questions


1. Common locking strategies

The reason why the lock strategy is called a strategy is that it is not a specific lock, but a series of features for the reference of the lock implementer, which is also very helpful for ordinary programmers to use locks reasonably.

1.1 Optimistic lock vs pessimistic lock

Optimistic lock:

It is assumed that the data generally does not generate concurrency conflicts, so when the data is submitted for update, it will formally detect whether the data has concurrency conflicts. If concurrency conflicts are found, an error message will be returned to let the user decide what to do .

Pessimistic lock:

Always assume the worst case, think that others will modify it every time you get the data, so you will lock it every time, so that others will block until they get the lock if they want to get the data.

For example: classmate A and classmate B want to ask the teacher a question:

  • Classmate A thinks that the teacher must be busy, so he will give the teacher a message first: "Is the teacher busy? I will ask you a question at 10 in the morning." (equivalent to locking operation) will come only after getting an affirmative reply, If you get a negative reply, wait for a while and ask the teacher next time. This is a pessimistic lock
  • Classmate B thinks that the teacher must be relatively idle, so he will go directly to the teacher (no lock, direct access to resources). If the teacher is indeed relatively idle, then the problem will be solved. If the teacher is busy, then he will not bother the teacher Come back next time (although every lock is added, data access conflicts can be identified). This is an optimistic lock.

The advantages and disadvantages of these two ideas depend on the specific implementation scenarios:

  • If the current teacher is really busy, then it is suitable to use pessimistic locks. Using optimistic locks will lead to "many trips in vain" and consume additional resources.
  • If the current teacher is relatively free, then it is suitable to use optimistic locks, and use pessimistic locks to improve efficiency

Synchronized initially uses an optimistic lock strategy, and when lock competition occurs frequently, it will automatically switch to a pessimistic lock strategy.

Classmate C (equivalent to Synchronized), began to think that "the teacher should be relatively free", and would directly ask the teacher if he had any questions.

But after I came directly to the teacher several times, I found that the teachers are very busy, so next time I come to ask the teacher, I will send a message first, before deciding whether to ask questions.


 An important function of optimistic locking is to detect data access conflicts, we can introduce a version number to resolve.

Assuming that multi-threading is required to modify the "account balance"

Let the current account balance be 100, introduce a version number version, the initial value is 1, and we stipulate that "the submitted version must be greater than the current record version to update the balance "

1) Thread A reads information at this time (version = 1, balance = 100), and thread B also reads information (version = 1, balance = 100)

2) Thread A deducts 50 (100-50) from its account during operation, and thread B deducts 20 (100-20) from its account.

3) Thread A completes the modification work, and writes the data version number +1 (version = 2) together with the account deduction balance (balance = 50) into the memory.

4) Thread B completes the operation, also adds 1 to the version number (version = 2), and tries to submit data to the memory (balance = 80), but by comparing the version number, it is found that the version number of the data submitted by operator B is 2, and the database The version number of the record is also 2, which does not satisfy the optimistic locking strategy of " the commit version number must be greater than the current version of the record to perform an update ". So this operation is considered to have failed.


1.2 Read-write lock:

Between multiple threads, data readers will not have thread safety, but both data writers and readers need mutual exclusion. If the same lock is used in both cases, there will be Great performance loss, so read-write locks are generated.

Readers-writer lock (readers-writer lock), as the name implies, needs to additionally indicate the read and write intent when performing locking operations. Readers are not mutually exclusive, while writers require mutual exclusion with anyone .

There are two main operations for a thread to access data: read data and write data

  • The ReentrantReadWriteLock.ReadLock class represents a read lock, and this object provides lock / unlock methods for locking operations.
  • The ReentrantReadWriteLock.writeLock class represents a write lock, and this object also provides lock / unlock methods for locking and unlocking.

in:

  • There is no mutual exclusion between read lock and read lock.
  • Between write lock and write lock, mutual exclusion.
  • Between read lock and write lock. Mutual exclusion.

Tips : As long as "mutual exclusion" is involved, there is thread suspension and waiting. Once the thread is suspended, it is unknown when it will be called again. Therefore, it is important to improve efficiency to reduce the chance of "mutual exclusion" as much as possible . way.

Synchronized is not a read-write lock.


1.3 Heavyweight locks vs lightweight locks

The core feature of the lock is "atomicity", and such a mechanism is traceable to the source provided by hardware devices such as the CPU.

  • The CPU provides "atomic instructions".
  • The operating system implements the mutex mutual exclusion lock based on the atomic instruction of the CPU.
  • Based on the mutex provided by the operating system, the JVM implements keywords and classes such as synchronized and ReentrantLock .

 Heavyweight lock : The locking mechanism relies heavily on the mutex provided by the OS

  • Lots of kernel-mode user switching
  • It is easy to cause thread scheduling

The cost of these two operations is relatively high, but once it involves switching between the user state and the kernel state , it means "a vicissitudes of life".

Lightweight lock : The locking mechanism does not use mutex as much as possible, but tries to complete it in user mode code as much as possible. If it is really uncertain, use mutex again.

  • A small amount of user mode switching
  • Not easy to cause thread scheduling

User Mode vs Kernel Mode

Suppose you go to the bank to handle business:

Outside the window, handling business at the ATM machine is equivalent to the user mode, and the time cost of the user mode is relatively controllable.

In the window, the staff handles it, which is the kernel state, and the time cost of the kernel state is uncontrollable.

If you need to communicate with the staff repeatedly and queue up again, the efficiency is very low.

Synchronized is a lightweight lock at the beginning. If the lock conflict is serious, it will become a heavyweight lock. The lightweight lock of synchronized is implemented based on spin lock , and the heavyweight lock is implemented based on pending lock .


1.4 Spin Lock (Spin Lock)

According to the above conclusion, the thread will enter the blocked state after the strong lock fails, give up the CPU, and it will take a long time to be scheduled again.

But in reality, although the strong lock fails, the lock will be released after a while, and there is no need to give up the CPU. At this time, a spin lock is needed to deal with such problems.

Spin lock pseudocode:

while(抢锁(lock) == 失败){}

If the lock acquisition fails, try to acquire the lock again immediately until the lock is acquired, the first lock acquisition fails, and the second attempt will come in a very short time.

Therefore, once the lock is released by other threads, the lock can be acquired at the first time.

Spin lock vs pending wait lock

When Xiao Ming went to ask the teacher a question, the teacher said: Wait a moment, this meeting is already giving a lecture to other students.

Suspend the waiting lock : go back to do your own thing, after a long, long time, the teacher suddenly sent a message, "There will be free time" (note, during this long time interval, the teacher may have finished speaking to multiple students problem)

Spinlock : Stand at the door of the teacher's office, once the last classmate comes out, you can immediately seize the opportunity to ask questions.

Spin lock is a typical implementation of lightweight lock:

  • Advantages: No giving up the CPU, no thread blocking scheduling involved, once the lock is released, the lock can be acquired at the first time
  • Disadvantages: If the lock is occupied by other threads for too long, it will continue to consume CPU resources. (CPU resources are not consumed when hanging and waiting)

The lightweight locks in synchronized are formed by means of spin locks .


1.5 Fair lock vs unfair lock

Suppose there are three threads A, B, and C. A successfully acquires the lock, B and C both try to acquire the lock, but the acquisition fails, blocking and waiting. So what happens when the A thread releases the lock?

Fair lock : Follow "first come, first served". B comes first before C, then when A is released, B may acquire the lock before C.

Unfair lock : Does not follow "first come, first served". Both B and C may acquire the lock.

Tips:

  • The thread scheduling inside the operating system is random. If there are no additional restrictions, the lock is an unfair lock. If you want to implement a fair lock, you need to rely on additional data structures . Record the order of threads.
  • There is no difference between fair locks and unfair locks, the key lies in the applicable scenarios.

synchronized is an unfair lock.


1.6 Reentrant lock vs non-reentrant lock

Reentrant lock literally means "re-entrant lock", which allows a thread to acquire the same lock multiple times .

For example, if there is a lock operation in a recursive function, if the lock does not block itself during the recursive process, then the lock is a reentrant lock (recursive lock).

In Java, all locks starting with Reentrant are reentrant, and all ready-made Lock implementation classes provided by JDK, including the synchronized keyword, are all reentrant.

The mutex provided by the LInux system is a non-reentrant lock.

Synchronized is a reentrant lock.


1.7 Related Interview Questions

1. How do you understand optimistic locking and pessimistic locking, and how to implement them?

  • Pessimistic locking believes that multiple threads access the same shared variable with a high probability of conflict, and will actually lock it before each access to the shared variable.
  • Optimistic locking believes that the conflict probability of multiple threads accessing the same shared variable is not high, and does not actually lock, but directly tries to access the data. While accessing, identify whether the current data has an access conflict.
  • The implementation of optimistic locking can introduce a version number, and use the version number to identify whether the current data access conflicts.

2. Introduce read-write lock?

The read-write lock is to lock the read operation and the write operation separately.

The read operation and the read operation are not mutually exclusive.

Mutually exclusive between read and write operations.

Mutually exclusive between write operations and write operations.

The longest usage scenario of read-write lock is "frequent read, infrequent write".

3. What is a spin lock, why use the spin lock strategy, and what are the disadvantages?

A spin lock is a lightweight lock. If the lock acquisition fails, the lock will be acquired in an infinite loop until the lock is acquired. Therefore, once the lock is released by other threads, it can be acquired at the first time.

Compared to hanging waiting for a lock :

Advantages : CPU resources are not given up, and the lock can be acquired as soon as the lock is released, which is more efficient. It is very efficient when the lock holding time is relatively short.

Disadvantage : If the lock is held for a long time, it will waste CPU resources.

4. Is synchronized a reentrant lock?

It is a reentrant lock, which means that two consecutive locks will not cause deadlock.

The implementation method is to record the identity of the thread holding the lock in the lock, and a counter (recording the number of locks), if it is found that the currently locked thread is the thread holding the lock, the counter will be incremented directly.

Guess you like

Origin blog.csdn.net/liu_xuixui/article/details/128862990