Lock锁——java

Lock

Starting from JDK5.0, java provides a more powerful thread synchronization mechanism-through the explicit definition of synchronization lock objects to achieve synchronization

1.Lock interface

public interface Lock

LockImplementations provide more extensive locking operations than can synchronizedbe obtained methods and reports. They allow a more flexible structure, can have completely different characteristics, can support multiple related Conditionobjects.

Lock is a tool that controls access to shared resources through multiple threads. Generally, a lock provides exclusive access to shared resources: only one thread can obtain the lock at a time and all access to the shared resources needs to obtain the lock first. However, some locks can allow concurrent access to shared resources, such as a ReadWriteLockread lock.

The synchronizedmethod or report provides access to the use of implicit monitor locks related to each object, but the acquisition and release of all locks of the force occurs in a structured way: when multiple locks are acquired, they must be released in the reverse order, And all the locks must be released with the same lexical scope as they are acquired.

While the mechanism synchronizedmethods and statements make it easier to monitor lock programs, and help to avoid many common programming errors with locks, sometimes it is necessary to use locks in a more flexible way. For example, some algorithms that traverse concurrently accessed data structures need to use "hand-in-hand" or "chain lock": you get the lock node A, then node B, then release A and obtain C, then release B and obtain D, etc. . The Lockimplementation of this interface enables the use of technologies that allow one lock to be acquired and issued in different scopes, and allows multiple locks to be acquired and issue any commands.

With this increased flexibility comes additional responsibilities. The block is not locked and the structure and synchronizedmethod of unlocking the lock are automatically released when the report is released. In most cases, the following idioms should be used:

 Lock l = ...;
 l.lock();
 try {
    
    
   // access the resource protected by this lock
 } finally {
    
    
   l.unlock();
 }

When locking and unlocking are in different scopes, care must be taken to ensure that all the code is executed, and the execution of the holding lock is subject to the final attempt or attempt to seize to ensure that the lock is released.

LockThe implementation provides additional functions to provide a non-blocking synchronizedmethod of trying to acquire a lock and the use of statements ( tryLock()), an attempt to acquire a lock can be interrupted ( lockInterruptibly(), and an attempt to acquire a lock, timeout ( tryLock(long, TimeUnit)).

A Lockclass can also provide behavior and semantics, which are completely different from the implicit monitor lock, such as guarantee order, non-reentrant usage, or deadlock detection. If an implementation provides such specialized semantics, then the implementation of these semantics must record these semantics.

Note that Lockinstances are just ordinary objects and can also be used as targets in synchronizeddeclarations. Obtaining an Lockinstance monitoring lock does not matter to any specified method of calling the instance lock(). This is a suggestion, in order to avoid confusion, you do not use Lockexamples like this , except in your own implementation.

Unless otherwise specified, passing any parameter nullvalue will result in a NullPointerExceptionthrow.

Memory synchronization

All Lockimplementations must implement the same memory synchronization semantics provided by built-in monitoring locks, such as The Java Language Specification (17.4 Memory Model) :

  • A successful lockoperation has the same memory synchronization effect as a successful locking action.
  • A successful unlockoperation has the same memory synchronization effect as a successful unlocking operation.

Successfully lock and unlock operations, and can lock/unlock operations without any memory synchronization effects.

2. Problem introduction

Insecurity between threads:

Take a ticket as an example:

public class TestLock {
    
    

    public static void main(String[] args) {
    
    
        TestLock2 lock1 = new TestLock2();
//        TestLock2 lock2 = new TestLock2();
//        TestLock2 lock3 = new TestLock2();

        //多个线程需要操作同一个对象
        new Thread(lock1).start();
        new Thread(lock1).start();
        new Thread(lock1).start();
    }

}

class TestLock2 implements Runnable{
    
    

    int ticketNumber = 10;

    public void run() {
    
    
        while (true){
    
    
            if (ticketNumber>0){
    
    
                try {
    
    
                    Thread.sleep(100);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                System.out.println("获得票:"+(ticketNumber--));
            }else {
    
    
                break;
            }
        }
    }
}

Insecure (resource access violation)

Insert picture description here

The previous solution was handled by synchronized (Object):

public class TestLock {
    
    

    public static void main(String[] args) {
    
    
        TestLock2 lock1 = new TestLock2();
//        TestLock2 lock2 = new TestLock2();
//        TestLock2 lock3 = new TestLock2();

        //多个线程需要操作同一个对象
        new Thread(lock1).start();
        new Thread(lock1).start();
        new Thread(lock1).start();
    }

}

class TestLock2 implements Runnable{
    
    

    int ticketNumber = 10;

    public synchronized void run() {
    
    

        while (true){
    
    
            if (ticketNumber>0){
    
    
                try {
    
    
                    Thread.sleep(100);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                System.out.println("获得票:"+(ticketNumber--));
            }else {
    
    
                break;
            }
        }
    }
}

The result is correct:

Insert picture description here

3. Lock solution-ReentrantLock class

public class ReentrantLock
extends Object
implements Lock, Serializable

A reentrant mutex Lockhas the same basic behavior and semantics for monitoring locks using implicit synchronizedmethod and reporting access, but the extension function.

One ReentrantLockis successfully locked by the thread at the end, but it has not been unlocked yet. A thread call lockwill return, successfully acquiring the lock, when the lock is not owned by another thread. If the current thread already owns the lock, the method will return immediately. This can be isHeldByCurrentThread()checked using methods , and getHoldCount().

The constructor of this class accepts an optional fairness parameter. When set true, contention, lock favors granted access to the longest waiting thread. Otherwise, this lock does not guarantee any specific access sequence. Programs that use fair locks accessed by many threads may show lower overall throughput (ie, slower than those using the default settings, and tend to be much slower), but there are smaller differences in time to acquire locks and lack of guaranteed starvation . Please note that the fairness of locks does not guarantee the fairness of thread scheduling. Therefore, one of the many threads that use a fair lock may acquire it multiple times in a row, while the other active threads are not progressing instead of the lock currently held. Also note that the irregular tryLock()method does not respect the fairness setting. If the lock is available, it will succeed even if other threads are waiting.

It is the recommended practice to always follow a call lockwith a tryblock, the most typical is to build before/after etc:

 class X {
    
    
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
    
    
     lock.lock();  // block until condition holds
     try {
    
    
       // ... method body
     } finally {
    
    
       lock.unlock()
     }
   }
 }

In addition to implementing the Lockinterface, this class defines some methods publicand protectedmethods for checking the status of the lock . Some of these methods are just useful instruments and monitoring.

This type of serialization behaves in the same way as a deserialized built-in lock: the lock is in an unlocked state, regardless of its state when serialized.

This lock supports a maximum of 2147483647 recursive locks in the same thread. Attempting to Errorexceed this limit will result from locking the method.

The concept of reentrant locks

Reentrant lock means that the thread can repeatedly lock the same lock without being blocked, which can avoid deadlock.

ReentrantLock就是可重入锁

Realization -the key is to put the unlock in the finally statement to prevent deadlock when an error occurs.

public class TestLock {
    
    

    public static void main(String[] args) {
    
    
        TestLock2 lock1 = new TestLock2();
//        TestLock2 lock2 = new TestLock2();
//        TestLock2 lock3 = new TestLock2();

        //多个线程需要操作同一个对象
        new Thread(lock1).start();
        new Thread(lock1).start();
        new Thread(lock1).start();
    }

}

class TestLock2 implements Runnable{
    
    

    int ticketNumber = 10;

    //定义lock锁
    private final ReentrantLock lock = new ReentrantLock();

    public void run() {
    
    

        while (true){
    
    
            try {
    
    
                lock.lock();//加锁
                if (ticketNumber>0){
    
    
                    try {
    
    
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    System.out.println("获得票:"+(ticketNumber--));
                }else {
    
    
                    break;
                }
            }catch (Exception e){
    
    
                e.printStackTrace();
            }finally {
    
    
                lock.unlock();//释放锁
            }

        }
    }
}

4. The difference between synchronized and lock

  • lock is an explicit lock (manual release), synchronized is an implicit lock (automatic release out of scope)
  • lock can only lock code blocks, synchronized can lock methods
  • Using Lock locks, JVM will spend less time, better performance, and has better scalability (ReentrantLock reentrant lock)
  • Use sequence Lock>Sync code block>Sync method

Guess you like

Origin blog.csdn.net/joey_ro/article/details/109963481