このコード セグメントでは、パイプを作成し、一方の端に Scanner を接続し、もう一方の端に PrintStream を接続して、複数のコンシューマ スレッドとプロデューサー スレッド間で通信します。次に、3 つのスレッドを作成して開始します。
最初のスレッドはコンシューマ スレッドです。Scanner をチェックして、テキスト行が利用可能かどうかを確認し、それを消費して stdout に出力し、数ミリ秒間スリープしてから繰り返します。消費するものがない場合は、それに関するメッセージを出力し、スリープして繰り返します。
このコード セグメントの 2 番目のスレッドは何もしません。詳細については、以下をご覧ください。
2.5 3 番目のスレッドが起動するまでに 3 秒の遅延があります。
- 3 番目のスレッドはプロデューサーであり、最初のスレッドが消費するテキスト メッセージを生成するだけです。メッセージを生成し、スリープします
public static void main(String[] args) throws IOException
{
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream(pis);
Scanner scan = new Scanner(pis);
PrintStream ps = new PrintStream(pos);
new Thread()
{
public void run()
{
int x = 0;
while (true)
{
x++;
if (scan.hasNextLine())
{
System.out.println("pulled: " + scan.nextLine());
} else
{
if (x % 100 == 0)
{
System.out.println("no data to pull");
}
}
try
{
sleep(10);
} catch (InterruptedException ex) { }
}
}
}.start();
new Thread()
{
public void run()
{
}
}.start();
try
{
sleep(3000);
} catch (InterruptedException ex) { }
new Thread()
{
public void run()
{
int x = 0;
while (true)
{
x++;
ps.println("hello: " + x);
try
{
sleep(1000);
} catch (InterruptedException ex) {}
}
}
}.start();
}
出力(期待どおり):
pulled: hello: 1
pulled: hello: 2
pulled: hello: 3
pulled: hello: 4
pulled: hello: 5
pulled: hello: 6
また、scan.nextLine() がブロックされていることにも注意してください (利用可能なデータがないことを示すメッセージがないため、データは「途中」であっても常に「利用可能」です)。
ここで、2 番目のスレッドの本文を、最初のスレッドが消費するテキストを生成するコードに置き換えると、次のようになります。
new Thread()
{
public void run()
{
ps.println( "Interfere");
}
}.start();
次に、最初のスレッドの no data 節をトリガーし始めます。
pulled: Interfere
no data to pull
no data to pull
no data to pull
no data to pull
no data to pull
no data to pull
no data to pull
no data to pull
したがって、2 番目のスレッドが PrintStream オブジェクトを使用してメッセージを生成し始めると、パイプで何か問題が発生し、コンシューマ スレッドは反対側でメッセージを見つけることができなくなります。
そして今、物事は奇妙になります。たとえば、2 番目のスレッドを非常に長いループに投げ込むことで、2 番目のスレッドが終了しないようにすると、パイプが詰まることはありません。
new Thread()
{
public void run()
{
ps.println("interfere");
for ( long i = 0; i < 10000000000L; i++ );
System.out.println("done interfering" );
}
}.start();
出力:
pulled: interfere
pulled: hello: 1
pulled: hello: 2
done interfering
pulled: hello: 3
pulled: hello: 4
pulled: hello: 5
pulled: hello: 6
したがって、3 番目のスレッドが生成を開始する前に 2 番目のスレッドが終了した場合、最初のスレッドは 3 番目のスレッドからメッセージを受け取ることはないと思います。ただし、3 番目のスレッドが生成を開始するまで 2 番目のスレッドがハングアップした場合、すべてが期待どおりに進みます。
何が起きてる?2 番目のスレッドは、終了時にパイプ/ストリームを閉じていますか (または、パイプ/ストリームに対して他のアクションを実行していますか)? もしそうなら、なぜですか?また、2 番目のスレッドが終了する前に 3 番目のスレッドがパイプ/ストリームの使用を開始した場合、パイプ/ストリームを閉じないように見える (または何らかのアクションを実行しない) のはなぜですか? 2 番目のスレッドがメッセージを生成し、3 番目のスレッドが開始する前に終了したときに、このコードを期待どおりに「機能させる」方法はありますか (つまり、最初のスレッドがいずれかまたは両方のプロデューサー スレッドによって生成されたものを消費するようにする)。
背景: これは、複数のクライアントが 1 つのプロデューサー スレッドからのメッセージを消費するシステムの重要なコンポーネントを要約したものです。ただし、プロデューサー スレッドは、すべてのクライアント スレッドが準備完了を通知するまで開始できません。クライアント スレッドごとに、準備ができているかどうかを問い合わせる別のスレッドがあります。すべてのクライアント スレッドが準備が整ったことを通知すると、プロデューサー スレッドが起動されます。スレッドをストリーム経由で通信させようとしているので、後でそれらを複数のコンピューターに分散し、ソケットを使用してパイプをセットアップし、基になるコードに最小限の変更を加えることができます。ここでも別のソリューション戦略を自由に提案してください。ただし、上記のソリューションが機能しない理由を理解したいと思います。