1

私のマルチスレッド アプリケーションには、複数のスレッドを作成するメイン クラスがあります。メインクラスは、いくつかのスレッドを開始した後に待機します。私が作成した実行可能なクラスは、Web サービスを呼び出して、ファイルの一覧を取得し、ファイルを取得し、ファイルを削除します。スレッドが完了すると、メイン クラスに再度実行するように通知します。私の問題は、しばらくは機能しますが、おそらく 1 時間ほど後に、ログに表示される出力から run メソッドの最後に到達することです。Java プロセスはまだ実行されていますが、ログに表示されている内容に基づいて何も実行していません。

主なクラス メソッド:

主な方法

while (true) {

    // Removed the code here, it was just calling a web service to get a list of companies

    // Removed code here was creating the threads and calling the start method for threads

    mainClassInstance.waitMainClass();
}

public final synchronized void waitMainClass() throws Exception {
//        synchronized (this) {
           this.wait();
//        }  
}

public final synchronized void notifyMainClass() throws Exception {
//        synchronized (this) {
           this.notify();
//        }  
}

もともとインスタンスで同期を行っていましたが、メソッドに変更しました。また、Web サービス ログまたはクライアント ログにエラーが記録されていません。私の仮定は、待機して通知が間違っているか、情報の一部が欠落しているということです。

実行可能なスレッド コード:

run メソッドの最後に

// This is a class member variable in the runnable thread class
mainClassInstance.notifyMainClass();

別のスレッドを作成する必要がない限り、メインクラスを実行したくないため、待機および通知プロセスを実行した理由。

メイン クラスの目的は、スレッドを生成することです。このクラスには、スレッドの作成と終了を永遠に実行するための無限ループがあります。

無限ループの目的は、企業リストを継続的に更新することです。

4

1 に答える 1

2

トリッキーな待機/通知機能から、Java プラットフォームの高レベルの並行機能の 1 つに移行することをお勧めします。ExecutorServiceはおそらく、すぐに必要な機能を提供します。( CountDownLatchも使用できますが、より配管工です) コードをテンプレートとして使用して例をスケッチしてみましょう。

ExecutorService execSvc = Executors.newFixedThreadPool(THREAD_COUNT);

while (true) {

    // Removed the code here, it was just calling a web service to get a list of companies
    List<FileProcessingTask> tasks = new ArrayList<FileProcessingTask>();
    for (Company comp:companyList) {
        tasks.add(new FileProcessingTask(comp));
    }
    List<Future<FileProcessingTask>> results = execSvc.invokeAll(tasks); // This call will block until all tasks are executed.
    //foreach Future<FileProcessingTask> in results: check result
}

class FileProcessingTask implements Callable<FileResult> {  // just like runnable but you can return a value -> very useful to gather results after the multi-threaded execution
    FileResult call() {...}
}

------- コメントの後に編集 ------

getCompanies()呼び出しですべての会社を一度に取得でき、処理中にそのリストを継続的にチェックする必要がない場合は、最初にすべての作業項目を作成し、それらを executor サービスに一度に送信することでプロセスを簡素化できます。

List<FileProcessingTask> tasks = new ArrayList<FileProcessingTask>();
    for (Company comp:companyList) {
        tasks.add(new FileProcessingTask(comp));
    }

理解しておくべき重要なことは、executorService は提供されたコレクションを実行するタスクの内部キューとして使用するということです。最初のタスクを受け取り、それをプールのスレッドに渡し、結果を収集し、その結果を結果コレクションに配置してから、キュー内の次のタスクを受け取ります。

タスクが実行される (消費される) と同時に新しい作業が生成されるプロデューサー/コンシューマー シナリオ (cfr コメント) がない場合、このアプローチは、多数のスレッド間で処理作業を並列化するのに十分なはずです。簡単な方法で。

新しい作業の検索が作業の処理からインターリーブされて発生する必要がある追加の要件がある場合は、質問で明確にする必要があります。

于 2012-11-22T21:03:34.690 に答える