スレッドは、あなたが考えている方法で再利用することはできません。あなたが何をしているのかは、実際に番号を生成するたびに新しいスレッドを開始することです。その時点では、計算のためのスレッドをまったく持たないこともあります。
質問のサブテキストは、常に3つのスレッドが実行中であることを示しているようです。それはあなたの問題を本質的に2つの消費者の状況を持つ生産者にします。唯一の捉え方は、消費者が知的に行動し、必要なデータだけを取り上げなければならないということです。
ジェネレータコードは処理コードと同様に正しいトラックでオフになっています。主な変更点は、新しいスレッドを開始する代わりに、notify
(実際にはnotifyAll
)のスレッドがEven
とOdd
のスレッドを実行する必要があることです。
毎秒1つのアイテムが生成されるので、実際には適切なキューは必要ありません。単一のInteger
オブジェクトは、消費されていない番号を保持するのに十分です。比較的古いアーキテクチャーでも処理に1秒以上かかる場合は、他の問題があります。 int
の代わりにInteger
を使用する理由は、待機中のスレッドにオブジェクトが既に消費されていることを伝える必要があるからです。この場合、返すnull
はかなり簡単な方法のようです。
class Producer implements Runnable, IntConsumer
{
private Integer buffer = null;
public void run()
{
new Random().ints().forEach(this);
}
public void accept(int i)
{
synchronized(this) {
this.buffer = i;
this.notifyAll();
}
try {
Thread.sleep(1000L);
} catch(InterruptedException ie) {
throw new RuntimeException("Producer interrupted!");
}
}
public Integer peek()
{
return this.buffer;
}
public synchronized Integer get()
{
Integer i = peek();
this.buffer = null;
return i;
}
}
生成される整数をpeek
とget
を介してアクセスすることができるフィールドに配置されることに注意してください。生成コードは、バッファをクリアするget
と同様に、プロデューサオブジェクトをロックします。これにより、一度に1つのエンティティのみが値を変更できるようになります。
通常、コンシューマとプロデューサは、共有スタックやキューオブジェクトなどを介して通信します。同期は、LinkedBlockingQueue
またはArrayBlockingQueue
のように、キューによっても行われます。この単純なケースでは、プロデューサはキュー機能を実装しているため、コンシューマはプロデューサを認識する必要があります。
コンシューマスレッドには、多くの共通機能があります。彼らはどちらもプロデューサの通知を待つ必要があり、ロックを取得し、次のオブジェクトが消費されていないかどうかをチェックし、それらに属しているかどうかを確認してから、消費するか待機します。エラーを回避するには、check-then-get操作全体をアトミックにする必要があることに注意してください。これらはすべて基本クラスで簡単にエンコードできます。このクラスは、番号を確認し、処理するための抽象メソッドがあります:
public abstract class Consumer implements Runnable
{
private final Producer producer;
public Consumer(Producer producer)
{
this.producer = producer;
}
public void run()
{
while(true) {
synchronized(this.producer) {
Integer i = this.producer.peek();
if(i != null && check(i)) {
this.producer.get();
process(i);
}
try {
this.producer.wait();
} catch(InterruptedException ie) {
throw new RuntimeException("Consumer interrupted!");
}
}
}
}
public abstract boolean check(int i);
public abstract void process(int i);
}
を具体的な実装は数入力のパリティかどうかを確認し、それらが適しているという数字を処理します。 check
とprocess
はInteger
の代わりにint
を受け入れることに注意してください。プロデューサが空であるかどうかを確認する責任はないことを強調してください。あなたが早期に終了するメインスレッドで問題が発生した場合は力main
を待つために、
public class Driver
{
public static void main(String[] args)
{
Producer prod = new Producer();
new Thread(new Even(prod)).start();
new Thread(new Odd(prod)).start();
new Thread(prod).start();
}
}
:最後に、ドライバーはこのすべてを結びつけるmain
方法を含む
public class Even extends Consumer
{
public Even(Producer producer)
{
super(producer);
}
public boolean check(int i)
{
return (i % 2) == 0;
}
public void process(int i)
{
System.out.println("x=" + i + "; x^2=" + (i * i));
}
}
public class Odd extends Consumer
{
public Odd(Producer producer)
{
super(producer);
}
public boolean check(int i)
{
return (i % 2) != 0;
}
public void process(int i)
{
System.out.println("x=" + i + "; x^3=" + (i * i * i));
}
}
プロデューサーが次のようなもので終わる(永遠に):
Thread t = new Thread(prod);
t.start();
t.join();
あなたは(少なくともLinux上)Ctrl+C
を押すまでのコードは、1秒に1回、次のような出力を生成します:
x=966307133; x^3=-1676528219
x=1271569805; x^3=-1481001707
x=456385150; x^2=-1701399036
x=-474801821; x^3=-813682373
x=-1046547765; x^3=927976307
x=-148577454; x^2=-185956796
x=-14905035; x^3=-1126539123
x=2071820769; x^3=-1426032223
x=1075272421; x^3=1952259101
x=-895041093; x^3=-978791741
x=-2054472224; x^2=3441664
...
なぜを再利用使用複数のスレッドでこれをやっていますか?あなたは、多くの同期問題に遭遇する可能性が高いです。なぜ、1つのスレッドに値を印刷させるだけではないのですか? – FredK
@FredK同期の問題を作成するために設計された割り当てのように聞こえます –
@John割り当てられましたが、私は割り当てを超えて考えています。 – Ben