スレッド間でデータをやり取りする簡単な方法の 1 つはBlockingQueue<E>
、パッケージにあるインターフェイスの実装を使用することjava.util.concurrent
です。
このインターフェイスには、さまざまな動作で要素をコレクションに追加するメソッドがあります。
add(E)
: 可能な場合は追加し、そうでない場合は例外をスローします
boolean offer(E)
: 要素が追加されている場合は true、そうでない場合は false を返します
boolean offer(E, long, TimeUnit)
: 要素の追加を試行し、指定された時間待機します
put(E)
: 要素が追加されるまで、呼び出しスレッドをブロックします
また、同様の動作で要素を取得するためのメソッドも定義します。
take()
: 要素が利用可能になるまでブロックします
poll(long, TimeUnit)
: 要素を取得するか、null を返します
私が最も頻繁に使用する実装はArrayBlockingQueue
、 、LinkedBlockingQueue
およびSynchronousQueue
です。
最初のArrayBlockingQueue
は固定サイズで、コンストラクターに渡されるパラメーターによって定義されます。
2 番目のLinkedBlockingQueue
はサイズに制限がありません。常にすべての要素を受け入れます。つまり、offer
すぐに true を返しadd
、例外をスローすることはありません。
3 つ目、そして私にとって最も興味深いのSynchronousQueue
は、まさにパイプです。サイズ 0 のキューと考えることができます。要素を保持することはありません。このキューは、他のスレッドが要素を取得しようとしている場合にのみ要素を受け入れます。逆に、要素をプッシュしようとしている別のスレッドがある場合にのみ、取得操作は要素を返します。
セマフォのみで同期を行うという宿題の要件を満たすために、SynchronousQueue について説明した内容に触発されて、非常によく似たものを書くことができます。
class Pipe<E> {
private E e;
private final Semaphore read = new Semaphore(0);
private final Semaphore write = new Semaphore(1);
public final void put(final E e) {
write.acquire();
this.e = e;
read.release();
}
public final E take() {
read.acquire();
E e = this.e;
write.release();
return e;
}
}
このクラスは、SynchronousQueue について説明したものと同様の動作を示すことに注意してください。
メソッドput(E)
が呼び出されると、同じメソッドへの別の呼び出しが最初の行でブロックされるように、空のままになる書き込みセマフォを取得します。このメソッドは、渡されたオブジェクトへの参照を格納し、読み取りセマフォを解放します。take()
このリリースでは、メソッドを呼び出すすべてのスレッドが続行できるようになります。
メソッドの最初のステップはtake()
、当然のことながら、他のスレッドが要素を同時に取得できないようにするために、読み取りセマフォを取得することです。要素が取得され、ローカル変数に保持された後 (演習: その行 E e = this.e が削除されたらどうなるでしょうか? )、メソッドは書き込みセマフォを解放しますput(E)
。ローカル変数に保存されたものを返します。
重要な注意点として、渡されるオブジェクトへの参照はプライベート フィールドに保持され、メソッドtake()
とメソッドput(E)
は両方ともfinalであることに注意してください。これは非常に重要であり、しばしば見逃されます。これらのメソッドが final でない場合 (またはさらに悪いことに、フィールドが非公開の場合)、継承クラスは動作を変更してコントラクトtake()
をput(E)
破ることができます。
最後に、次のようtake()
に使用すると、メソッドでローカル変数を宣言する必要がなくなります。try {} finally {}
class Pipe<E> {
// ...
public final E take() {
try {
read.acquire();
return e;
} finally {
write.release();
}
}
}
ここで、この例のポイントは、try/finally
経験の浅い開発者の間では見過ごされている の使用法を示すことです。明らかに、この場合、実際の利益はありません。
くそー、私はあなたのためにあなたの宿題をほとんど終えました。報復として、そしてセマフォに関する知識をテストするために、BlockingQueue コントラクトで定義された他のメソッドのいくつかを実装してみませんか? たとえば、offer(E)
メソッドとtake(E, long, TimeUnit)
!
幸運を。