javax.swing.SwingWorkerの問題
-
06-07-2019 - |
質問
swingsアプリケーションを作成しましたが、次のような問題が1つあります。
<!> quot; Thread-Main <!> quot;という名前のSwingWorkerスレッドを開始しました。イベントディスパッチスレッドから、GUIのJLabel参照を<!> quot; Thread-Main <!> quot;に渡しました。
<!> quot; Thread-Main <!> quot;から10個のスレッドを開始しました。
これで、10個のスレッドすべてでJLabelを更新する必要があります。
どうすればそれができますか?
SwingWorkerの10個のすべてのスレッドサブクラスを作成してからpublish(<!> quot; <!> quot;)メソッドを呼び出し、その<!> quot;に文字列を渡すことでこれを行うことができると誰かが教えてくれました。 publish <!> quot; <!> quot; Thread-Main <!> quot;
の次のメソッドにより、公開されたすべての文字列を収集します@Override
protected void process(List<String> labelStrings) {
String count = labelStrings.get(labelStrings.size() - 1);
label.setText(count); // label is a reference to a label in the GUI
}
- 上記のアプローチは正しいですか?
- 10個のスレッドはSwingWorkerのサブクラスですか?
- 他の方法はありますか?
解決
- いいえ-これは、スレッドの数を自分で制御したい場合、間違ったアプローチです。どうして?
SwingWorker
コードを見ると、内部で最大10スレッドを含むThreadPoolExecutor
を使用していることがわかります。多数のdoInBackground()
を同時に開始すると、これらはすべてこのexecutorを使用して実行されます。ただし、バックグラウンドスレッドを並列で実行するかどうかを直接制御することはできません。 - ポイント1を参照してください。
-
推奨される解決策は次のとおりです。
- 単一の
ExecutorService
を作成します。 -
CountDownLatch
メソッド内で、直接またはCompletionService
を使用して10個のスレッドを開始します。 -
Callable<String>
またはString
を使用して、マスタースレッド(つまり、JLabel
バックグラウンドスレッド)とワーカースレッドを同期します。
- 単一の
例
更新するJLabelを呼び出して宣言するワーカースレッドの数を定義します。
final int nThreads = 10;
JLabel myLbl = new JLabel();
実行する作業単位をdone()
として定義します。 Void
の結果は、process(String... chunks)
の更新に使用されます。
private static class MyCallable implements Callable<String> {
public String call() { ... }
}
ここで<=>を開始します。これにより、処理を行うために複数の並列ワーカーが開始されます。ワーカーは、<=>(したがって<=>タイプ)への呼び出しを介して結果を返しませんが、<=>を呼び出して、 中間の<=>結果をSwingスレッドにマーシャリングします。
new SwingWorker<Void, String>() {
// See method definitions below.
}.execute();
<=>を定義してワーカースレッドを開始し、<=>を使用して各結果をブロックします。
public Void doInBackground() throws Exception {
// Define executor service containing exactly nThreads threads.
ExecutorService execService = Executors.newFixedThreadPool(nThreads);
// Define completion service that will contain the processing results.
CompletionService compService = new ExecutorCompletionService(execService);
// Submit work to thread pool using the CompletionService. Future<String>
// instances will be added to the completion service's internal queue until complete.
for (int i=0; i<nThreads; ++i) {
compService.submit(new MyCallable());
}
// Take results from each worker as they appear and publish back to Swing thread.
String result;
while ((result = compService.take().get()) != null) {
publish(result);
}
}
今、<=>を実装して、呼び出されたときに<=>を単純に更新します。
public void process(String... chunks) {
if (chunks.length > 0) {
// Update label with last value in chunks in case multiple results arrive together.
myLbl.setText(chunks[chunks.length - 1]);
}
}
最後に<=>をオーバーライドして、例外をSwingスレッドにマーシャリングします。
public void done() {
try {
get(); // Will return null (as per Void type) but will also propagate exceptions.
} catch(Exception ex) {
JOptionPane.show ... // Show error in dialog.
}
}
他のヒント
たぶん簡単なアプローチは、GUIを更新するコードをSwingUtilities.invokeLater(...)メソッドでラップすることです。
編集:ラベルを更新したいときはいつでも、個々のスレッドで:
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
label.setText(...);
}
});
なぜ10個のスレッドが必要なのですか?余分なスレッドが1つ追加されないのはなぜですか?
本当の質問:解決しようとしている問題は何ですか?
直接的な質問に答えるには
1)はい、それは正しいアプローチです 2)はい、スレッドはSwingWorkerである必要があります(netbeansを使用している場合は、SwingWorkerのサブクラスでもあるタスクも使用できます)
3)edtとは別のスレッドが必要な場合。次に、swingworkerを使用する必要があります。それがそれを行うための方法です。
がんばって!