並行プログラミングソリューション
開発プロセスで並行性の問題が発生することがよくあります。並行性の問題が発生した場合、通常はロックを使用して問題を解決します。実際、ロックには次の2つの実装スキームがあります。
- 信号
- チューブ
チューブプロセスとセマフォは同等です。チューブプロセスを使用してセマフォを実現でき、セマフォを使用してチューブプロセスを実現することもできます。チューブプロセスは開発者にとってより使いやすいです。
信号
セマフォは、整数変数semと2つのアトミック操作で構成されるオペレーティングシステムの同期メカニズムです。
P(Prolaag、オランダ語は削減を試みます):sem-1、sem <0の場合、待機キューに入り、それ以外の場合は続行します
V(Verhoog、オランダ語の増加):sem + 1、sem <= 0の場合、待機中のスレッドがありますキュー、スレッドを起こします
sem変数は、初期化が完了したときにPV操作でのみ変更できます。この操作により、PV操作の原子性が保証されます。Pはブロックされる場合がありますが、Vはブロックされません。
コード
public class Semaphore {
private int sem;
private WaitQueue q;
public void P() {
sem--;
if (sem < 0) {
add this thread t to q;
// 阻塞线程
block(t);
}
}
public void V() {
sem++;
if (sem <= 0) {
remove a thread t from q;
// 唤醒线程
wakeup(t)
}
}
}
セマフォは2つのカテゴリに分類できます
- バイナリセマフォ:リソースの数は0または1です
- リソースセマフォ:リソースの数は負でない値です
セマフォには次の機能があります
- クリティカルセクション(一度に1つのスレッドのみがアクセスできる領域)への相互に排他的なアクセスを実現します。
- 条件付き同期を実現する
クリティカルセクションへの相互に排他的なアクセスを実現する
Semaphore mutex = new Semaphore(1);
mutex.P();
// do something
mutex.V();
セマフォの初期値は1でなければなりません、PV操作ペア使用
条件付き同期を実現する
Semaphore condition = new Semaphore(0);
// ThreadA,进入等待队列中
condition.P();
// ThreadB,唤醒等待线程 ThreadA
condition.V();
セマフォの初期値は0である必要があり、P操作の実行時にThreadAがブロックされ、V操作の実行時にThreadBが待機中のスレッドThreadAをウェイクアップします。
セマフォを使用してブロッキングキューを実装する
バイナリセマフォミューテックスを使用して相互に排他的なアクセスを実現
する2つのリソースセマフォnotFul、notEmptylを使用して条件付き同期を実現する
public class BlockingQueueUseSemaphore<T> {
private final Object[] items;
private Semaphore notFull;
private Semaphore notEmpty;
private Semaphore mutex;
private int putIndex;
private int takeIndex;
public BlockingQueueUseSemaphore(int capacity) {
this.items = new Object[capacity];
notFull = new Semaphore(capacity);
notEmpty = new Semaphore(0);
mutex = new Semaphore(1);
}
public void enq(T x) throws InterruptedException {
notFull.acquire();
mutex.acquire();
items[putIndex] = x;
if (++putIndex == items.length) {
putIndex = 0;
}
mutex.release();
notEmpty.release();
}
public T deq() throws InterruptedException {
notEmpty.acquire();
mutex.acquire();
T x = (T) items[takeIndex];
if (++takeIndex == items.length) {
takeIndex = 0;
}
mutex.release();
notFull.release();
return x;
}
}
チューブ
クリティカル領域のPV操作でセマフォをペアリングする問題を解決するために、チューブプロセスはペアリングされたPV操作をまとめて、オブジェクト指向の考え方とより一貫性のある新しい並行プログラミング方法を生成します。
条件変数の概念は監視プロセスで導入され、各共有変数は待機キューに対応します
同期は、同期キューが1つだけ含まれるモニターに基づいて実装され、待機キュー
AQSもモニターに基づいて実装されます。同期キューは1つだけ含まれますが、複数の待機キューを含めることができます。
モニターを使用してブロッキングキューを実装する
JavaでのAQSの設計アイデアは管理であるため、この機能を実現するためにAQSで関連するAPIを使用します
これは、モニターの1つの共有変数と2つの条件変数で実現できます。
public class BlockingQueueUseMonitor<T> {
private final Object[] items;
private final Lock lock;
private Condition notFull;
private Condition notEmpty;
private int count;
private int putIndex;
private int takeIndex;
public BlockingQueueUseMonitor(int capacity) {
this.items = new Object[capacity];
lock = new ReentrantLock();
notFull = lock.newCondition();
notEmpty = lock.newCondition();
}
public void enq(T x) throws InterruptedException {
lock.lock();
try {
while (count == items.length) {
// 等待队列不满
notFull.await();
}
items[putIndex] = x;
if (++putIndex == items.length) {
putIndex = 0;
}
count++;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public T deq() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
// 等待队列不空
notEmpty.await();
}
T x = (T) items[takeIndex];
if (++takeIndex == items.length) {
takeIndex = 0;
}
count--;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
リファレンスブログ
好文
[1] https://www.cnblogs.com/binarylei/p/12544002.html#26-aqs-%E5%92%8C-synchronized-%E5%8E%9F%E7%90%86
[2] https://www.codenong.com/cs109504287/
管程
[3] https://time.geekbang.org/column/article/86089式
量
[4] https://time.geekbang.org/column/article / 88499