読み取るファイルが1000個あり、いくつかの制限があるため、最大5個のファイルを並行して読み取りたいとします。そして、そのうちの1つが終了したらすぐに、新しいものを開始したいと思います。
ファイルのリストを持っているmain関数があり、1つのスレッドが終了するたびにカウンターを変更しようとしています。しかし、それは機能しません!
なにか提案を?
以下は主な関数ループです
for (final File filename : folder.listFiles()) {
Object lock1 = new Object();
new myThread(filename, lock1).start();
counter++;
while (counter > 5);
}
このようなスレッドを生成することは、進むべき道ではありません。 ExecutorService
を使用し、プールを5に指定します。すべてのファイルをBlockingQueue
または別のスレッドセーフコレクションなどに配置すると、実行中のすべてのファイルはpoll()
になります。意のままに。
public class ThreadReader {
public static void main(String[] args) {
File f = null;//folder
final BlockingQueue<File> queue = new ArrayBlockingQueue<File>(1000);
for(File kid : f.listFiles()){
queue.add(kid);
}
ExecutorService pool = Executors.newFixedThreadPool(5);
for(int i = 1; i <= 5; i++){
Runnable r = new Runnable(){
public void run() {
File workFile = null;
while((workFile = queue.poll()) != null){
//work on the file.
}
}
};
pool.execute(r);
}
}
}
ExecutorServiceをスレッドプールおよびキューとして使用できます。
ExecutorService pool = Executors.newFixedThreadPool(5);
File f = new File(args[0]);
for (final File kid : f.listFiles()) {
pool.execute(new Runnable() {
@Override
public void run() {
process(kid);
}
});
}
pool.shutdown();
// wait for them to finish for up to one minute.
pool.awaitTermination(1, TimeUnit.MINUTES);
Kylarの答えのアプローチは正しいものです。スレッドプールを最初から実装するのではなく、Javaクラスライブラリによって提供されるエグゼキュータクラスを使用します(ひどく)。
しかし、私はあなたの質問のコードとそれが機能しない理由を議論することが役立つかもしれないと思いました。 (私はあなたが私ができる限り省略した部分のいくつかを埋めました...)
_public class MyThread extends Thread {
private static int counter;
public MyThread(String fileName, Object lock) {
// Save parameters in instance variables
}
public void run() {
// Do stuff with instance variables
counter--;
}
public static void main(String[] args) {
// ...
for (final File filename : folder.listFiles()) {
Object lock1 = new Object();
new MyThread(filename, lock1).start();
counter++;
while (counter > 5);
}
// ...
}
}
_
さて、これの何が問題になっていますか?なぜそれが機能しないのですか?
最初の問題は、main
で同期を行わずにcounter
を読み書きしていることです。ワーカースレッドによっても更新されていると思います。それ以外の場合、コードは意味がありません。つまり、メインスレッドには、子スレッドによって行われた更新の結果が表示されない可能性が高いということです。言い換えると、while (counter > 5);
は無限ループになる可能性があります。 (実際、これはかなり可能性が高いです。JITコンパイラーは、_counter > 5
_が前の_counter++;
_ステートメントの後にレジスターに残っているcounter
の値をテストするだけのコードを生成できます。
2番目の問題は、while (counter > 5);
ループがリソースを非常に浪費していることです。あなたはJVMに変数をポーリングするように指示しています...そしてそれはこれを1秒間に数十億回実行する可能性があります... 1つのプロセッサ(コア)を完全に実行します。あなたはそれをすべきではありません。低レベルのプリミティブを使用してこの種のものを実装する場合は、JavaのObject.wait()
およびObject.notify()
メソッドを使用する必要があります。例えばメインスレッドは待機し、各ワーカースレッドが通知します。
新しいスレッドを作成するために使用している方法が何であれ、グローバルカウンターをインクリメントし、スレッドの作成の前後に条件ステートメントを追加します。制限に達した場合は、新しいスレッドを作成しないでください。ファイルをキュー(リスト)にプッシュする可能性があります。 ?)次に、スレッドが作成された後、キューにアイテムがある場合は、それらのアイテムを最初に処理するために、別の条件ステートメントを追加できます。