ロックの概念
ロックは、一般に、リソース・アクセス・モードを共有複数のスレッドを制御するために使用される、複数のスレッドが同時に共有リソースへのアクセスを防止するロック(そのような読み書きロックなどの共有リソースにアクセスするために複数のスレッドによって同時にいくつかのロックを可能にすることができます)。
JDK1.5前に、Javaはsynchronizedキーワード、ロック機能によって達成される:暗黙的に取得し、ロックを解除し、あまり柔軟性。
JDK1.5では、java.util.concurrent
パケットインタフェースとロック機能を実装してロック関連の実装クラスを追加しました。これは、synchronizedキーワード同様の機能との同期を提供していますが、より強力かつ柔軟次のように操作性が、ロック・タイムアウト・アクイジション・ロックなどを取得するために中断することができますロックを取得し、解放します:
プロパティ | 説明 |
---|---|
ロックを取得しようとする試みをノンブロッキング | ロックがロックを保持している別のスレッドによって獲得されていないこの時間が正常に取得された場合、現在のスレッドは、ロックを取得しようとすると、 |
ロックへのアクセスを中断することができます | スレッドは、割り込みに応答するためにロックを取得することができます(と同期応答が動作を中断しません) |
タイムアウト獲得ロック | 期限がまだ返されたロックを取得できない場合に、指定された期限前にロックを取得します。 |
インターフェイス特定の方法および解釈をロック:
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(); }
パッケージのjava.util.concurrent.locks
クラス図
ここで、
AbstractOwnableSynchronizer
、AbstractQueuedLongSynchronizer
、AbstractQueuedSynchronizer
シンクロナイザは、関連するコンテンツを達成するためのロックです。
ReentrantLock(重入锁)
そして、ReentrantReadWriteLock(重入读写锁)
特定のカテゴリです。
LockSupport
基本的なスレッドブロックとウェイクアップ機能を提供するユーティリティクラスです。
Condition
これは、使用スレッド間のマルチ状態待機/通知モードを達成することです。
原則シンクロナイザ
ALL
リエントラントロック:ReentrantLockの
名前が示すようにリエントラントロックは、再入ロックをサポートする:ロックを取得した後のスレッドのロックが再び遮られることなく取得することができます。
ReentrantLockのクラスは、この再入カスタム同期の組み合わせによって達成されるべきであり、加えて、ロックへのそのような公平なアクセス(要求したロックが同じであるとして取得順序をロックし、最長待機時間をサポートしていますロックを取得するために、最も高い優先度のスレッド)、またコンディション複数のバインディングをサポートしています。(synchronized
キーワードは暗黙的のような再入国、サポートsynchronized
修正再帰的な方法で、メソッドの実行を、実行スレッドは、まだ繰り返し、その状況が表示されないブロックし、ロック後にロックへのアクセスを得ました)。
ReentrantLockの再エントリーは、内部(ロックケースに非公平なアクセス)を達成するコードは以下の通りであります:
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の公正な内部ロック・コードは次のとおりです。
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; }
非ロック方式への公平なアクセスnonfairTryAcquire(int acquires)
よりも多くと比較hasQueuedPredecessors()
判定:メソッドがtrueを返す場合、前駆体ノードがあるかどうか、同期キュー現在のノード(ロックを取得したい、現在のスレッド)は、スレッドの現在のスレッドよりも古いがあることを意味しますロックを取得するための要求の後、ロックを獲得し続けるためにロック前駆体スレッドを取得し、解放する必要があります。
ロックは、スレッド切り替えの多くを犠牲にしてFIFO原理に従ってロックへの公平なアクセスを確保するため、
それがスレッド「飢餓」を起こすかもしれないが(すなわち、スレッドは、彼らがロックを取得する前に長い時間を待つ必要があります)不当なロックを、非常に小さなスレッドの切り替えができ高いスループットを確保。
読み書きロック:ReentrantReadWriteLock
ReentrantLock
同時に、(読み取りまたは書き込みか)一つのスレッドだけアクセスを許可します。読み書きロック手段:同時に、複数のスレッドが、読み取り、書き込み、および他のすべてのスレッド(読み取りまたは書き込みのいずれかが、ブロックされる)を可能にする操作がブロックされます。読み書きロックロックのペアを維持するために:読み取りと書き込みロックを、分離して読み、ロックを書き込み、同時排他ロックの一般的なパフォーマンスが大幅に比べて改善されている作ります。
Javaが実現され、ロックを書くReentrantReadWriteLock
次のように、その内部の状態を監視し、外部容易にするための多くの方法を提供し、読み取りロックにダウングレードすることができるロックを記述します:③ロックを劣化させ、エクイティを選択②、①再突入:それはサポートしています
int getReadLockCount()
返回当前读锁被获取的次数
注意:该次数并不等于获取读锁的线程数,
因为同一线程可以连续获得多次读锁,获取一次,返回值就加1,
比如,仅一个线程,它连续获得了n次读锁,那么占据读锁的线程数是1,但该方法返回n
int getReadHoldCount() 返回当前前程获取读锁的次数 boolean isWriteLock() 判断读锁是否被获取 int getWriteHoldCount() 返回当前写锁被获取的次数
使用例:
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:読み書きロックの原理を実装します
LockSupportツール
LockSupport
、最も基本的なスレッドブロックとウェイクアップ機能を提供し、公共の静的メソッドのセットを定義二つの方法があることを基本的なツールの同期コンポーネントを構築することである:
①にpark
始まる方法:ブロック、現在のスレッド
で② unpark
の方法の始まり:ウェイクブロックされたスレッド
void park()
阻塞当前线程,只有当前线程被中断或其他线程调用unpark(Thread thread),才能从park()方法返回 void parkNanos(long nanos) 阻塞当前线程,最长不超过nanos纳秒,返回条件在park()的基础上增加了超时返回 void parkUntil(long deadline) 阻塞当前线程,直到deadline这个时间点(从1970年开始到deadline时间的毫秒数) void unpark(Thread thread) 唤醒处于阻塞状态的thread线程
JDK1.6、このクラス増大void park(Object blocker)
、void parkNanos(Object blocker, long nanos)
、void parkUntil(Object blocker, long deadline)
従来の方法公園に比べ方法、識別するために使用されるブロッカ・オブジェクトよりも多くのオブジェクトに現在のスレッドが(ブロッキングオブジェクト)待つ主にトラブルシューティングおよびシステムについて、監視は、(スレッドダンプのために、情報ブロックオブジェクトを提供することができる)の代わりに、元の公園法を用いることもできます。
条件インターフェース
任意のJava
オブジェクトは、(で定義された監視プロセスの設定しているjava.lang.Object
。) wait()、wait(long timeout)、notify()、notifyAll()
これらの方法とsychronized
組み合わせて使用、待機/通知モードを実現することができるが。
Condition
インターフェースは、同様の監視方法を提供し、Lock
併せて使用、待機/通知モードを実現することができます。
次のような違いは次のとおりです。
比較項目 | オブジェクトのモニター方法 | 調子 |
---|---|---|
前置条件 | オブジェクトのロックを取得 | コールLock.lockは、()(ロック→Lock.newConditionを取得するために呼び出す)条件オブジェクトを取得します。 |
呼ばれます | などのObject.waitとして直接呼び出し、() | このようcondition.awaitとして直接呼び出し、() |
待ち行列の数 | 1 | もっと |
現在のスレッドがロックを解除し、待機状態に入ります | サポート | サポート |
現在のスレッドがロックを解除し、待機状態に入り、待機状態は、割り込みに応答しません | サポートしていません。 | サポート |
現在のスレッドがロックを解除し、タイムアウト待ち状態に入ります | サポート | サポート |
現在のスレッドがロックを解除し、将来の時点に待機状態に入ります | サポートしていません。 | サポート |
キュー内で待機しているスレッドを覚まします | サポート | サポート |
キュー内で待機中のすべてのスレッドを覚まします | サポート | サポート |
方法は以下の通りである:(条件一般条件)は、メンバ変数としてオブジェクトます
説明:現在のスレッドがロックを解放した後、現在のスレッドの呼び出しを待つ()メソッドをし、他のスレッドが(シグナルを呼び出したときにここで待っ)メソッドは、現在のスレッドに伝え、現在のスレッドがのawait()メソッドから返され、すでに復帰する前にロックを獲得していません(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(); }
使用条件は、有界のブロッキングキュー実現例を:キューが空の場合は、キュー内の新しい要素があるまで、キューは、操作ブロックを現在のスレッドを取得します。キューがいっぱいになったときに、キュー挿入操作は、スレッドインサートをブロックします。空席になるまでキューに起こります。(実際には、この例では、簡略化されたバージョンArrayBlockingQueue
)
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:コンディション実装分析
参照
ほとんどの場合、JDKで参照の注釈部分の「Java並行プログラミングの芸術」から。
著者:maxwellyue
リンクします。https://www.jianshu.com/p/6e0982253c01
出典:ジェーン・ブック
著者によって予約ジェーン帳の著作権は、いかなる形で再現され、承認を得るために、作者に連絡して、ソースを明記してください。