java.util.concurrent.Locks Advanced Guide

1 Overview

In short, the lock is a more flexible than the standard sync blocks, a more sophisticated synchronization mechanism thread.

Since Java 1.5, Lock the interface has always existed. It is defined in java.util.concurrent.lock package, which provides a large locking operation.

In this article, we will explore the different implementations of the Lock interface and its application.

2. Lock difference between the block and Synchronized

Some differences between the use of sync blocks with LockLock API:

  • Sync block is entirely contained in the process - we will have API lock () and unlock () operation in a separate process
  • synchronized block does not support fairness, any thread can get locked once released, can not be specified preferences. By specifying a fair property, we can achieve fairness in the Lock API. It ensures the longest waiting threads can access lock
  • If the thread can not access the sync block, the thread will be blocked. Said lock tryLock () API provided by the method. Only when the thread is available and does not hold any other thread, the thread will get locked. This reduces the thread waits for a lock blocking time
  • In the "wait" state for the thread to access the sync block can not be interrupted. Said lock lockInterruptibly API provides a method of (), which can be used to interrupt the thread, which is waiting for the lock

3. Lock API

Let's look at the method of Lock:

  • void lock () - Gets locking (if available); If the lock is not available, the thread will be blocked until the lock is released
  • void lockInterruptibly () - This is similar to lock (), but it allows the blocked thread is interrupted and executed by throwing the java.lang.InterruptedException recovery
  • boolean tryLock () - This is a lock () non-blocking version of the method; it will attempt to acquire a lock immediately and return true if the lock is successful
  • boolean tryLock (long timeout, TimeUnit timeUnit) - This is similar to tryLock (), except it give up before trying to get Lock wait timeout given
  • void unlock () - Unlock Lock instance

You should always unlock a locked instance to avoid deadlock situations. Recommended use locking block should contain try / catch and finally blocks:

Lock lock = ...; 
lock.lock();
try {
    // access to the shared resource
} finally {
    lock.unlock();
}

In addition to Lock the interface, we have a ReadWriteLock interface that maintains a lock, one for read-only operations and one for writing. As long as no write, read lock can be maintained simultaneously by multiple threads.

ReadWriteLock declaration acquisition method of reading or writing lock:

  • Lock readLock () - returns the lock for reading
  • Lock writeLock () - Returns the lock for writing

4. Lock achieve

4.1 ReentrantLock

ReentrantLock class implements the Lock interface. It provides the same concurrency semantics and memory, such as using a synchronous access method and the implicit statement monitor lock, with extended functionality.

Let's see how we can use ReenrtantLock synchronization:

public class SharedObject {
    //...
    ReentrantLock lock = new ReentrantLock();
    int counter = 0;
 
    public void perform() {
        lock.lock();
        try {
            // Critical section here
            count++;
        } finally {
            lock.unlock();
        }
    }
    //...
}

We need to ensure that the try-finally block package lock () and unlock () call to avoid deadlock situations.

Let's look at tryLock () works:

public void performTryLock(){
    //...
    boolean isLockAcquired = lock.tryLock(1, TimeUnit.SECONDS);
     
    if(isLockAcquired) {
        try {
            //Critical section here
        } finally {
            lock.unlock();
        }
    }
    //...
}

In this case, the call tryLock () thread will wait for a second, and if the lock is not available then give up waiting.

4.2 ReentrantReadWriteLock

ReentrantReadWriteLock ReadWriteLock class implements the interface.

Let's look at the rules ReadLock or thread gets WriteLock of:

Read Lock - If no threads get locked or write request it, multiple threads can acquire a read lock
write lock - if there is no thread is reading or writing, only one thread can acquire the write lock
Let's look at how you can use ReadWriteLock:

public class SynchronizedHashMapWithReadWriteLock {
 
    Map<String,String> syncHashMap = new HashMap<>();
    ReadWriteLock lock = new ReentrantReadWriteLock();
    // ...
    Lock writeLock = lock.writeLock();
 
    public void put(String key, String value) {
        try {
            writeLock.lock();
            syncHashMap.put(key, value);
        } finally {
            writeLock.unlock();
        }
    }
    ...
    public String remove(String key){
        try {
            writeLock.lock();
            return syncHashMap.remove(key);
        } finally {
            writeLock.unlock();
        }
    }
    //...
}

For both writing method, we need to use a write lock to surround the critical region, only one thread can access it:

Lock readLock = lock.readLock();
//...
public String get(String key){
    try {
        readLock.lock();
        return syncHashMap.get(key);
    } finally {
        readLock.unlock();
    }
}
 
public boolean containsKey(String key) {
    try {
        readLock.lock();
        return syncHashMap.containsKey(key);
    } finally {
        readLock.unlock();
    }
}

For both reading method, we need to use a read lock to surround the critical section. If no write operation is in progress, multiple threads can access this section.

4.3 StampedLock

StampedLock introduced in Java 8. It also supports read-write lock. However, the lock acquisition method returns a check for releasing the lock or lock is still valid stamp:

public class StampedLockDemo {
    Map<String,String> map = new HashMap<>();
    private StampedLock lock = new StampedLock();
 
    public void put(String key, String value){
        long stamp = lock.writeLock();
        try {
            map.put(key, value);
        } finally {
            lock.unlockWrite(stamp);
        }
    }
 
    public String get(String key) throws InterruptedException {
        long stamp = lock.readLock();
        try {
            return map.get(key);
        } finally {
            lock.unlockRead(stamp);
        }
    }
}

Another function is to provide StampedLock optimistic locking. Most of the time, read operations do not need to wait for the write operation to complete, so no full-blown read lock.

Instead, we can upgrade the lock to read:

public String readWithOptimisticLock(String key) {
    long stamp = lock.tryOptimisticRead();
    String value = map.get(key);
 
    if(!lock.validate(stamp)) {
        stamp = lock.readLock();
        try {
            return map.get(key);
        } finally {
            lock.unlock(stamp);               
        }
    }
    return value;
}

5. Conditions

The condition class provides a thread wait for some ability to implement key elements of the situation.

When a thread to gain access to the critical region but did not perform the necessary conditions for its operation, this situation may occur. For example, readers can access the shared thread lock queue, the queue still does not have any data to be consumed.

Traditionally, Java provides a thread wait for the exchange (), notify () and notifyAll () method. Conditions similar mechanism, but in addition, we can specify multiple criteria:

public class ReentrantLockWithCondition {
 
    Stack<String> stack = new Stack<>();
    int CAPACITY = 5;
 
    ReentrantLock lock = new ReentrantLock();
    Condition stackEmptyCondition = lock.newCondition();
    Condition stackFullCondition = lock.newCondition();
 
    public void pushToStack(String item){
        try {
            lock.lock();
            while(stack.size() == CAPACITY) {
                stackFullCondition.await();
            }
            stack.push(item);
            stackEmptyCondition.signalAll();
        } finally {
            lock.unlock();
        }
    }
 
    public String popFromStack() {
        try {
            lock.lock();
            while(stack.size() == 0) {
                stackEmptyCondition.await();
            }
            return stack.pop();
        } finally {
            stackFullCondition.signalAll();
            lock.unlock();
        }
    }
}

6 Conclusion

In this article, we have seen different implementations of Lock StampedLock class interfaces and newly introduced. We also discuss how to use the Condition class to handle multiple conditions.

image

Welcome to the number of public attention: " Java confidant " public concern number, reply " 1024 " you know, receive a free 30 classic programming books . I am concerned about the progress with 100,000 programmers. Java knowledge is updated daily Oh, look forward to your arrival!

image

Guess you like

Origin blog.csdn.net/feilang00/article/details/87912123