Locks Package structure level

The Lock interface

The method signature describe
void lock(); Acquire locks (never die)
boolean tryLock(); Get the lock (dabble)
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; Get lock (not waiting)
void lockInterruptibly() throws InterruptedException; Acquire locks (at the mercy of others)
void unlock(); Release the lock
Condition newCondition();

Code examples:

Public class GetLockDemo {// static Lock Lock =new ReentrantLock(true); Static Lock Lock = new ReentrantLock(); Public static void main(String[] args) throws InterruptedException { Thread Thread = new Thread (() - > {/ / the child Thread locks (undead endlessly) System. Out. The println ("begain to get lock...");
              lock.lock();
              System.out.println("succeed to get lock..."); // Boolean result = lock.tryLock(); // System.out.println("Did you get the lock?"+ result); // try {// Boolean result1 = lock.tryLock(5, timeunit.seconds); // try {// Boolean result1 = lock.tryLock(5, timeunit.seconds); // System.out.println("Did you get the lock?"+ result1); // } catch (InterruptedException e) { // e.printStackTrace(); // try {// system.out.println (// system.out.println)"start to get lock Interruptibly");
              //                lock.lockInterruptibly();
              //              } catch (InterruptedException e) {
              //                e.printStackTrace();
              //                System.out.println("dad asked me to stop..."); / /}}); thread.start(); Thread.sleep(10000L); lock.unlock(); }}Copy the code

Conclusion:

  • The lock () the most commonly used
  • The lockInterruptibly() method is generally more expensive, and some implementation classes may not implement the lockInterruptible() method. Use interrupts only if you really need them, and read the implementation class’s description of the method before using them.

Condition

The wait(), notify(), and notifyAll() methods of Object are used in conjunction with synchronized to wake up one or more threads. Condition is used in conjunction with Lock, providing multiple wait sets and more precise control (the underlying is the Park /unpark mechanism);

Collaborative way Deadlock mode 1 (lock) Deadlock mode 2 (Wake up, then suspend) note
suspend/resume A deadlock A deadlock deprecated
wait/notify Die lock A deadlock Used only for synchronized keywords
park/unpark A deadlock Die lock
condition Die lock A deadlock

Example of condition code:

public class ConditionDemo {

  static Lock lock = new ReentrantLock();

  static Condition condition = lock.newCondition();

  public static void main(String[] args) throws InterruptedException {
    Thread thread =
        new Thread(
            () -> {
              lock.lock();
              System.out.println("condition.await()");
              try {
                condition.await();
                System.out.println("here i am..."); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); }}); thread.start(); Thread.sleep(2000L); lock.lock(); condition.signalAll(); lock.unlock(); }}Copy the code

ReetrantLock

A ReentrantLock is a ReentrantLock that can be acquired multiple times by the same thread

ReentrantLock implementation principle analysis

  1. ReentrantLock requires an owner to label the thread as having acquired the lock, a count to record the number of times the lock was applied, and a waiters wait queue to store the list of threads that didn’t get the lock
  2. If count is 0, the lock is not occupied
  3. Then the CAS operation is used to snatch the lock
  4. If the lock is captured, count is incremented by 1 and owner is set to a reference to the current thread
  5. If count is not 0 and owner refers to a reference to the current thread, the value of count is incremented by 1
  6. If count isn’t 0 and owner doesn’t point to a reference to the current thread, put the thread into a wait queue
  7. If the CAS lock grab fails, put the thread into the wait queue
  8. When the thread finishes using the lock, the lock it holds is released. When the lock is released, the value of count is reduced by 1. If count is 0, the owner is set to NULL
  9. If count is not 0, the thread waiting on the head of the queue is woken up to snatch the lock

Example code for manually implementing ReentrantLock:

Public class MyReentrantLock implements Lock {private AtomicInteger count = new AtomicInteger(0); Private AtomicReference<Thread> owner = new AtomicReference<>(); Private LinkedBlockingDeque<Thread> Waiters = new LinkedBlockingDeque<>(); @Override public booleantryLock() {// check if count is 0, if count! Int ct = count.get();if(ct ! If (count+=1) {if (count+=1) {if (count+=1) {if (count+=1) {if (count+=1)if (owner.get() == Thread.currentThread()) {
        count.set(ct + 1);
        return true;
      } else{// If the current thread is not occupied, mutually exclusive, lock grab failure,return false
        return false; }}else{// If count=0, the lock is not occupied. CAS (0, 1) is used to snatch the lockif(count.compareAndSet(ct, ct + 1)) {count.compareAndSet(ct, ct + 1)) {count.compareAndSet(ct, ct + 1);return true;
      } else {
        return false;
      }
    }
  }

  @Override
  public void lock() {// Try to grab the lockif(! TryLock ()) {// Waiters. Offer (thread.currentThread ()); / / spinfor(; ;) Thread head = waiters. Peek ();if(head == thread.currentThread ()) {// Try locking againif(! TryLock ()) {// If lockgrab fails, suspend the thread and continue waiting for locksupport.park (); }else{// Work properly, please work in the queue. Poll ();return; }}else{// If not the queue head, suspend the thread locksupport.park (); } } } } public booleantryUnlock() {// Check if the current thread owns the lock, if not, throw an exceptionif(owner.get() ! = Thread.currentThread()) { throw new IllegalMonitorStateException(); }elseInt ct = count.get(); int ct = count.get(); int nextc = ct - 1; count.set(nextc); // Check if count is 0if (nextc == 0) {
        owner.compareAndSet(Thread.currentThread(), null);
        return true;
      } else {
        return false;
      }
    }
  }

  @Override
  public void unlock() {// Try to release the lockifThread = waiters. Peek ();if(thread ! = null) { LockSupport.unpark(thread); } } } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return false;
  }

  @Override
  public void lockInterruptibly() throws InterruptedException {}

  @Override
  public Condition newCondition() {
    returnnull; }}Copy the code

synchronized VS Lock

Synchronized:

  • Easy to use, semantic clarity, where to point where
  • Provided by the JVM, provides multiple optimizations (lock coarsening, lock elimination, biased locking, lightweight locking)
  • Locks are released by the virtual machine without human intervention, reducing the possibility of deadlocks

Disadvantages: pessimistic exclusive lock, unable to achieve advanced lock functions such as fair lock, read and write lock, etc

Lock: Advantages: Synchronized can achieve advanced Lock functions such as fair Lock, read and write Lock, etc., but also can achieve more functions

Disadvantages: You need to manually release the unlock lock, which may cause deadlock

Conclusion: Both are reentrant locks. Synchronized can be likened to point-and-shoot cameras, providing fixed functions, while Lock can be likened to unilateral ones, which can be adjusted according to needs