This is the 15th day of my participation in Gwen Challenge

Author: Flower Gie

Wechat official account: Java development zero to one

preface

We have updated many chapters in the multithreading series, and we strongly recommend that you learn in order:

Squat can also enter the factory “multithreading series article directory

We’ve learned about synchronized locks and how they work, but what type of lock do you think synchronized locks are? Pessimistic locks, reentrant locks, uninterruptible locks, etc. Why are there so many types of locks?

The body of the

Lock the overview

Locks can be classified from different perspectives, and these categories are not mutually exclusive, such as a person can be both a doctor and a father. Classification overview as shown below:

Optimistic and pessimistic locks

  • concept

This is actually the same as the personality of different people. Optimistic lock corresponds to optimistic and cheerful friends, who always see the good side of things; The pessimistic lock corresponds to a life in which everything is considered to be the worst.

Pessimistic lock is also called mutex synchronization lock. In order to ensure the correctness of the result, it will lock the data after obtaining it every time. Therefore, when other threads also access the data, it will enter the blocking state, which prevents other threads from accessing the data and ensures data security. Java we commonly used Synchronized and RenntrantLock are pessimistic locks, in many places in the database used this locking mechanism, such as row lock, table lock, read lock, write lock, etc..

An optimistic lock, also known as a non-mutex synchronous lock, considers that no other thread will interfere with its data processing, so it will not lock the data. If one thread is modifying data without interference from other threads, the changes will be executed normally. If the data has been modified by another thread, the current thread will discard or report errors to ensure that the data is correct. Common examples of pessimistic locking are atomic classes, concurrent sets, and so on.

We have also analyzed the atomic class, interested friends suggest to see AtomicInteger usage and source code introduction.

  • Optimistic lock implementation

    1. Version number mechanism

      Add a version field to the data table to indicate the number of times the data is updated. When data is read out, the version number is read together. After each update, add one to the version number and save it to the table record. For example, the current version is compared with the version in the database during the update operation. If the submitted data version is larger, the update operation is performed. Otherwise, the data is considered to be expired and the update operation is retried until the update succeeds.

    2. CAS

      Compare and swap, literally compare and swap. CAS contains three operands: the target data memory location (V), the expected value for comparison (A), and the new value to be written (B). When data is modified, the memory location V is compared with the expected value A. If the two values are equal, the memory location V is changed to the new value B. Otherwise, no operation is performed.

      Development:

      1. CAS contains two operations, comparison and transformation, but it is atomic and is guaranteed atomicity by the CPU at the hardware level.
      2. Many CAS operations are spin, that is, unsuccessful operations are retried until the operation succeeds.
  • Comparison of advantages and disadvantages

    “Synchronized” is not a Synchronized lock, but a Synchronized pessimistic lock.

    1. Pessimistic lock defect
      • When a thread enters a pessimistic lock, if the thread is permanently blocked (such as a deadlock), the other threads waiting for the lock can only wait and never execute.
      • Limit the number of concurrent requests:
    2. Optimistic lock defect
      • ABA problem: If A variable is first read with A value, its value is changed to B, and then changed back to A, the CAS operation will assume that it has never been changed.
      • Long spin time and high overhead: If the data is not modified successfully, it will continue to loop until it succeeds, which will bring very large execution overhead to the CPU.
      • Functional limitations: CAS can only guarantee atomicity for a single variable. If we need to guarantee atomicity for a piece of code, optimistic locking is no longer suitable.
  • Usage Scenario Selection

Pessimistic lock or optimistic lock is not to replace each other, there is no superior or inferior, but they are suitable for different scenarios.

  1. Optimistic locking is better for situations where there is less contention for resources (with fewer thread collisions). Synchronized synchronization is used for thread blocking and wake up switch, as well as switching between user mode and kernel mode, which consumes additional CPU resources. CAS, on the other hand, is hardware based, does not need to enter the kernel, and does not need to switch threads, so it can achieve higher performance.

  2. In the case of serious resource contention (serious thread conflict), CAS spins more frequently, which wastes CPU resources and is less efficient than pessimistic locking.

Reentrant, not reentrant lock

  • concept

A non-reentrant lock is a lock that has been acquired in the current thread execution and will be blocked if it is acquired again.

Let’s use wait/notify to design a non-reentrant lock (optionally CAS + spin) :

// Wait and notify implement non-reentrant locks
public class NonReentrantLockDemo1  {
    // Whether the record is locked
    private volatile boolean locked = false;

    public synchronized void lock(a) {
        // When one thread succeeds in acquiring the lock, the other threads enter the wait state
        while (locked) {
            try {
                wait();
            } catch(InterruptedException e) { e.printStackTrace(); }}// Lock succeeded, locked is set to true
        locked = true;
    }
    / / releases the lock
    public synchronized void unlock(a) {
        locked = false; notify(); }}/ / test class
class ThreadDemo implements Runnable{
    NonReentrantLockDemo lock = new NonReentrantLockDemo();

    public static void main(String[] args) {
        new Thread(new ThreadDemo()).start();
    }
    /** * method 1: call method 2 */
    public void method1(a) {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " method1()");
            method2();
        } finally{ lock.unlock(); }}/** * Method 2: Obtain obj lock ** / before printing
    public void method2(a) {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " method2()");
        } finally{ lock.unlock(); }}@Override
    public void run(a) { method1(); }}Copy the code

Test results:

The result shows that when the thread executes method1, it has already acquired the lock, and as long as the lock is not released, no other code block can use it to lock. When the thread calls method2 again, method2 also needs to get a lock in order to execute, resulting in deadlocks, which cause problems with re-entrant a lock, called non-reentrant locks.

A reentrant lock, also called a recursive lock, means that the inner method automatically acquires the lock when the same thread acquires the lock in the outer method. Because “squat can also enter the factory” multi-threaded series -synchronized in-depth principle of the end of the lock has carried on a detailed explanation, so do not repeat.

  • Code sample

Here’s a simple example of ReentrantLock reentrant:

private static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) {
    System.out.println(lock.getHoldCount());
    lock.lock();
    System.out.println(lock.getHoldCount());
    lock.lock();
    System.out.println(lock.getHoldCount());
    lock.unlock();
    System.out.println(lock.getHoldCount());
    lock.unlock();
    System.out.println(lock.getHoldCount());
}
Copy the code

Print result:

0
1
2
1
0
Copy the code

The result shows that the same thread can acquire the ReentrantLock again without releasing the lock.

  • Reentrant advantages
  1. Avoid deadlock
  2. Improved encapsulation

conclusion

The above is part of the introduction of the lock, theoretical relatively strong, but this part of the content is very important, otherwise the follow-up others ask you what the pessimistic lock, even if you have used, but you may not understand this pessimistic lock is what, causing the scene was awkward.

The rest of locking will be covered in the next chapter.

Pay attention to avoid getting lost

The above is the whole content of this issue, if there is any mistake, please leave a message for advice, thank you very much. I’m GieGie, feel free to leave a comment and we’ll see you next time at 🦮.

The article continues to update, you can wechat search Java development zero to one for the first time to read, and you can get interview materials learning video, interested partners welcome to pay attention to, learn together, ha 🐮🥃.

Original is not easy, how can you bear to whoring for nothing, if you think this article is a little useful to you, thank old tie for this article to like, comment or forward, because this will be my output more quality articles of power, thank you!

Reference links:

Ifeve.com/java-art-re…

www.cnblogs.com/ConstXiong/…

Blog.csdn.net/qq_20499001…

www.cnblogs.com/yeya/p/1358…