Before Java 5.0, there were only synchronized (built-in locks) and volatile. After Java 5.0, the display lock ReentrantLock was introduced.
ReentrantLock Overview
ReentrantLock is a reentrant lock. It is different from the built-in lock. It requires explicit locking and unlocking every time it is used, and provides more advanced features: fair lock, timed lock, conditional lock, and pollable lock. , can interrupt the lock. It can effectively avoid the liveness problem of deadlock. ReentrantLock implements
Lock interface:
Copy the code code as follows:
public interface Lock {
//Block until the lock is obtained or interrupted
void lock();
//Block until the lock is obtained or an exception is thrown when interrupting
void lockInterruptibly() throws InterruptedException;
//Only obtain when the lock is available, otherwise return directly
boolean tryLock();
//Only obtain the lock if it is available within the specified time, otherwise return directly and throw an exception when interrupted
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
//Return a condition bound to this lock
Condition newCondition();
}
Lock use
Copy the code code as follows:
Lock lock = new ReentrantLock();
lock.lock();
try{
//update object status
}finally{
//Note here, there must be a finally code block to unlock
//Otherwise, it is easy to cause deadlock and other activity problems.
lock.unlock();
}
ReentrantLock Features
Polling locks and timed locks
Pollable and timeable lock requests are implemented through the tryLock() method, which is different from unconditional acquisition of locks. ReentrantLock can have a flexible fault-tolerant mechanism. Many cases of deadlock are caused by sequential locks, and different threads are trying to acquire locks. It blocks when locking and does not release the lock it already holds, eventually causing a deadlock. When the tryLock() method tries to obtain a lock, if the lock is already held by another thread, it will return immediately according to the setting method instead of blocking and waiting. At the same time, it will release the lock it holds after returning. You can return it according to the returned As a result, retry or cancel is performed to avoid deadlock.
fairness
The ReentrantLock constructor provides two options: fair lock and unfair lock (default). So-called fair locks, threads will acquire locks in the order in which they issue requests, and queue jumping is not allowed; but for unfair locks, queue jumping is allowed: when a thread requests to acquire a lock, if the lock is available, then This thread will skip the waiting thread in the queue and acquire the lock. We generally want all locks to be unfair. Because when performing locking operations, fairness will greatly reduce performance due to the overhead of thread suspension and thread recovery. Consider a situation: Thread A holds a lock, thread B requests the lock, so thread B is suspended; when thread A releases the lock, thread B will be awakened, so it tries to acquire the lock again; at the same time, thread C Also requests to acquire this lock, then the C thread is likely to acquire, use and release this lock before the B thread is fully awakened. This is a win-win situation. The moment B acquires the lock (B can acquire the lock only after it wakes up) is not delayed. C acquires the lock earlier, and the throughput is also improved. In most cases, the performance of unfair locks is higher than the performance of fair locks.
Lock acquisition operations can be interrupted
The lockInterruptibly method acquires the lock while remaining responsive to interrupts, so there is no need to create other types of uninterruptible blocking operations.
ReadWriteLockReadWriteLock
ReentrantLock is a standard mutex lock. Only one thread can hold the lock at a time. The read-write lock is different. It exposes two Lock objects, one of which is used for read operations and the other for write operations.
Copy the code code as follows:
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading.
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing.
*/
Lock writeLock();
}
Optional implementation:
1. Release priority
2. Read thread jumps in line
3. Reentrancy
4.Downgrade
5.Upgrade
ReentrantReadWriteLock implements the ReadWriteLock interface, and the constructor provides two creation methods: fair lock and unfair lock. Read-write locks are suitable for situations where there is more reading and less writing, and can achieve better concurrency.
The sample copy code is as follows:
public class ReadWriteMap<K, V> {
private Map<K, V> map;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
public ReadWriteMap(Map<K, V> map) {
this.map = map;
}
public V get(K key) {
readLock.lock();
try {
return map.get(key);
} finally {
readLock.unlock();
}
}
public void put(K key, V value) {
writeLock.lock();
try {
map.put(key, value);
} finally {
writeLock.unlock();
}
}
}