[Multithreading and concurrency]: lock in Java

The concept of lock

Lock is used to control a plurality of threads shared resource access mode, in general, a plurality of threads simultaneously lock prevents access to shared resources (but may allow for some locks concurrently by multiple threads to access shared resources, such as a read-write lock).

Before JDK1.5, Java is achieved by the synchronized keyword lock function: implicitly acquire and release locks, but less flexible.

In the JDK1.5, java.util.concurrentpacket interface and added Lock relevant implementation class that implements the lock function. It provides synchronization with the synchronized keyword similar functionality, but more powerful and flexible: acquire and release locks operability can be interrupted to acquire the lock timeout acquire locks, etc., as follows:

characteristic description
Non-blocking attempt to acquire the lock The current thread tries to acquire the lock, if this time the lock is not acquired by another thread that holds the lock is successfully acquired and
Can interrupt access to the lock The thread can acquire the lock to respond to the interrupt (and synchronized response will not interrupt the operation)
Timeout acquire locks Acquire a lock before the specified deadline, if the deadline is still unable to acquire the lock is returned.

Lock the interface specific methods and interpretation:

public interface Lock {

    /**
     * 获取锁
     * 
     * 如果当前线程无法获取到锁(可能其他线程正在持有锁),则当前线程就会休眠,直到获取到锁
     */
    void lock(); /** * 可中断地获取锁 * * 如果如果当前线程无法获取到锁(可能其他线程正在持有锁),则当前线程就会休眠, * 直到发生下面两种情况的任意一种: * ①获取到了锁 * ②被其他线程中断 */ void lockInterruptibly() throws InterruptedException; /** * 尝试非阻塞地获取锁 * * lock()和lockInterruptibly()在获取不到锁的时候,都会阻塞当前线程,直到获取到锁 * 而该方法则不会阻塞线程,能立即获取到锁则返回true,获取不到则立即返回false * * 该方法的常用方式如下: * * Lock lock = ...; * if (lock.tryLock()) { * try { * // manipulate protected state * } finally { * lock.unlock(); * } * } else { * // perform alternative actions * }} * * 这种使用方式,可以保证只在获取到锁的时候才去释放锁 */ boolean tryLock(); /** * 超时获取锁 * * 当前线程在以下三种情况下会返回: * ①当前线程在超时时间内获取到了锁,返回true * ②当前线程在超时时间内被中断,返回false(即该方法可以响应其他线程对该线程的中断操作) * ③超时时间结束,没有获取到锁,返回false */ boolean tryLock(long time, TimeUnit unit) throws InterruptedException; /** * 释放锁 */ void unlock(); /** * 获取与该锁绑定的Condition * * 当前线程只有在获得了锁,才能调用Condition的wait()方法(表示我已经到了某一条件), * 调用Condition的wait()方法之后,当前线程会释放锁 */ Condition newCondition(); } 

Package java.util.concurrent.locksclass diagram

java.util.concurrent.locks classes and interfaces .png

Where:
AbstractOwnableSynchronizer, AbstractQueuedLongSynchronizer, AbstractQueuedSynchronizera synchronizer is a lock to achieve relevant content.
ReentrantLock(重入锁)And ReentrantReadWriteLock(重入读写锁)is a specific category.
LockSupportIs a utility class that provides the basic thread blocks and wake-up functions.
ConditionIt is to achieve inter-thread multi-condition waiting / notification mode used.


The principle synchronizer

ALL


Reentrant lock: ReentrantLock

Reentrant lock, as the name implies, is to support the re-entry lock: that of a thread after obtaining the lock, the lock can be acquired without being blocked again.
ReentrantLock class is to be achieved by a combination of a custom synchronizer this reentrancy, in addition, supports such equitable access to the lock (lock acquisition order as requested lock is the same, the longest waiting time the highest priority thread to acquire the lock), also supports multiple bindings Condition. ( synchronizedKeyword implicitly support the re-entry, such as synchronizedmodified recursive method, when the method execution, execution threads still repeatedly gained access to the lock after lock, blocking their situation does not appear).

ReentrantLock reentry achieve internal (non-equitable access to the lock case) code is as follows:

final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } }else if (current == getExclusiveOwnerThread()) { //如果是当前持有锁的线程再次获取锁,则将同步值进行增加并返回true int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } 

ReentrantLock fair internal lock codes are as follows:

protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } 

Equitable access to non-lock method nonfairTryAcquire(int acquires)compared to more than a hasQueuedPredecessors()judgment: synchronous queue the current node (the current thread that wants to acquire a lock) whether there is a precursor node, if the method returns true, it means that there are older than the current thread to thread after the request to acquire the lock, it is necessary to obtain and release the lock precursor thread to continue to acquire locks.

Lock to ensure fair access to the lock according to the FIFO principle at the expense of a lot of thread switching;
unfair lock although it may cause thread "Hunger" (ie, a thread may need to wait a long time before they get locked), but very little thread switching can ensure greater throughput.


Read-Write Lock: ReentrantReadWriteLock

ReentrantLockAt the same time, allowing only one thread access (whether read or write). The read-write lock means: at the same time, allowing multiple threads to read, write and all the other threads (either read or write, will be blocked) operation will be blocked. Read-write locks to maintain a pair of locks: read and write locks, read by separating and write locks, making the general performance of concurrent exclusive lock has been greatly improved in comparison.

Write lock in Java is the realization ReentrantReadWriteLockthat it supports: ① reentry; ② choose Equity; ③ degrade lock: write lock may be downgraded to a read lock, which provides a number of methods to facilitate external monitoring its internal state, as follows:

int getReadLockCount()
返回当前读锁被获取的次数
注意:该次数并不等于获取读锁的线程数,
因为同一线程可以连续获得多次读锁,获取一次,返回值就加1,
比如,仅一个线程,它连续获得了n次读锁,那么占据读锁的线程数是1,但该方法返回n

int getReadHoldCount() 返回当前前程获取读锁的次数 boolean isWriteLock() 判断读锁是否被获取 int getWriteHoldCount() 返回当前写锁被获取的次数 

Example of use:

public class Cache{

    //非线程安全的HashMap private static Map<String, Object> map = new HashMap<>(); //读写锁 private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); //读锁 private static Lock readLock = reentrantReadWriteLock.readLock(); //写锁 private static Lock writeLock = reentrantReadWriteLock.writeLock(); /** * 获取key对应的value * * 使用读锁,使得并发访问该方法时不会被阻塞 */ public static final Object get(String key){ readLock.lock(); try{ return map.get(key); }finally { readLock.unlock(); } } /** * 设置key对应的value * * 当有线程对map进行put操作时,使用写锁,阻塞其他线程的读、写操作, * 只有在写锁被释放后,其他读写操作才能继续 */ public static Object put(String key, Object value){ writeLock.lock(); try { return map.put(key, value); }finally { writeLock.unlock(); } } /** * 清空map * * 当有线程对map进行清空操作时,使用写锁,阻塞其他线程的读、写操作, * 只有在写锁被释放后,其他读写操作才能继续 */ public static void clear(){ writeLock.lock(); try { map.clear(); }finally { writeLock.unlock(); } } } 

TODO: implement the principle of read-write lock


LockSupport Tools

LockSupportDefines a set of public static methods that provide the most basic thread blocks and wake-up functions, is to build synchronization component of the basic tools that there are two methods:
① to parkmethods beginning with: blocks the current thread
② at unparkthe beginning of the method : wake blocked thread

void park()
阻塞当前线程,只有当前线程被中断或其他线程调用unpark(Thread thread),才能从park()方法返回 void parkNanos(long nanos) 阻塞当前线程,最长不超过nanos纳秒,返回条件在park()的基础上增加了超时返回 void parkUntil(long deadline) 阻塞当前线程,直到deadline这个时间点(从1970年开始到deadline时间的毫秒数) void unpark(Thread thread) 唤醒处于阻塞状态的thread线程 

In JDK1.6, this class increases void park(Object blocker), void parkNanos(Object blocker, long nanos), void parkUntil(Object blocker, long deadline)method, compared to the previous method park, more than a blocker object that is used to identify the current thread in the object (blocking objects) wait , primarily for troubleshooting and systems surveillance (for thread dump, can provide information blocking objects) can be used instead of the original park method.


Condition Interface

Any Javaobject has a set of monitor process (defined in java.lang.Objecta): wait()、wait(long timeout)、notify()、notifyAll()these methods and sychronizedused in conjunction, can achieve wait / notification mode.
ConditionThe interface provides a similar monitor method, and Lockused in conjunction, can achieve wait / notification mode.

The difference is as follows:

Comparison items Object Monitor Methods Condition
Pre-conditions Acquiring an object lock Call Lock.lock () call to acquire a lock → Lock.newCondition () Gets Condition objects
Called Direct calls, such as object.wait () Direct calls, such as condition.await ()
The number of waiting queue 1 More
The current thread releases the lock and enter the wait state stand by stand by
The current thread releases the lock and enter the wait state, the wait state does not respond to interrupts not support stand by
The current thread releases the lock and enter the timeout wait state stand by stand by
The current thread releases the lock and enter the wait state to a point in time in the future not support stand by
Wake up a thread waiting in the queue stand by stand by
Wake up all threads waiting in the queue stand by stand by

The method is as follows :( Condition General Condition will object as a member variable)
Description: The current thread calls await () method after the current thread releases the lock and wait here when the other thread calls signal () method tells the current thread, the current thread does not return from await () method, and before returning already acquired a lock ( re-acquire).

public interface Condition {
    
    /** * 当前线程进入等待状态直到被通知(signalled)或中断(interrupted) * * 如果当前线程从该方法返回,则表明当前线程已经获取了Condition对象所对应的锁 * * @throws InterruptedException */ void await() throws InterruptedException; /** * 与await()不同是:该方法对中断操作不敏感 * * 如果当前线程在等待的过程中被中断,当前线程仍会继续等待,直到被通知(signalled), * 但当前线程会保留线程的中断状态值 * */ void awaitUninterruptibly(); /** * 当前线程进入等待状态,直到被通知或被中断或超时 * * 返回值表示剩余时间, * 如果当前线程在nanosTimeout纳秒之前被唤醒,那么返回值就是(nanosTimeout-实际耗时), * 如果返回值是0或者负数,则表示等待已超时 * */ long awaitNanos(long nanosTimeout) throws InterruptedException; /** * 该方法等价于awaitNanos(unit.toNanos(time)) > 0 */ boolean await(long time, TimeUnit unit) throws InterruptedException; /** * 当前线程进入等待状态,直到被通知或被中断或到达时间点deadline * * 如果在没有到达截止时间就被通知,返回true * 如果在到了截止时间仍未被通知,返回false */ boolean awaitUntil(Date deadline) throws InterruptedException; /** * 唤醒一个等待在Condition上的线程 * 该线程从等待方法返回前必须获得与Condition相关联的锁 */ void signal(); /** * 唤醒所有等待在Condition上的线程 * 每个线程从等待方法返回前必须获取Condition相关联的锁 */ void signalAll(); } 

Use Condition achieve a bounded blocking queue example: When the queue is empty, the queue will get operation blocks the current thread until there are new elements in the queue; when the queue is full, the queue insertion operation will block the thread insert until a vacancy occurs in the queue. (In fact, this example is a simplified versionArrayBlockingQueue )

class BoundedBlockingQueue<T> {
    //使用数组维护队列
    private Object[] queue; //当前数组中的元素个数 private int count = 0; //当前添加元素到数组的位置 private int addIndex = 0; //当前移除元素在数组中的位置 private int removeIndex = 0; private Lock lock = new ReentrantLock(); private Condition notEmptyCondition = lock.newCondition(); private Condition notFullCondition = lock.newCondition(); private BoundedBlockingQueue() { } public BoundedBlockingQueue(int capacity) { queue = new Object[capacity]; } public void put(T t) throws InterruptedException { lock.lock();//获得锁,保证内部数组修改的可见性和排他性 try { //使用while,而非if:防止过早或意外的通知, //加入当前线程释放了锁进入等待状态,然后其他线程进行了signal, //则当前线程会从await()方法中返回,再次判断count == queue.length //todo:哪些情况下的过早或意外??? while (count == queue.length) { notFullCondition.await();//释放锁,等待队列不满,即等待队列出现空位 } queue[addIndex] = t; addIndex++; if (addIndex == queue.length) { addIndex = 0; } count++; notEmptyCondition.signal(); } finally { //确保会释放锁 lock.unlock(); } } @SuppressWarnings("unchecked") public T take() throws InterruptedException { lock.lock(); try { while (count == 0) { notEmptyCondition.await();//释放锁,等待队列不为空,即等待队列中至少有一个元素 } Object x = queue[removeIndex]; removeIndex++; if (removeIndex == queue.length) { removeIndex = 0; } count--; notFullCondition.signal();//通知那些等待队列非空的线程,可以向队列中插入元素了 return (T) x; } finally { //确保会释放锁 lock.unlock(); } } } 

TODO: Condition implementation analysis


reference

Mostly from "Art of Java concurrent programming", explanatory notes part of the reference in the JDK.



Author: maxwellyue
link: https: //www.jianshu.com/p/6e0982253c01
Source: Jane book
Jane book copyright reserved by the authors, are reproduced in any form, please contact the author to obtain authorization and indicate the source.

Guess you like

Origin www.cnblogs.com/xiaoshen666/p/11258543.html