javax.swing.swingworker의 문제
-
06-07-2019 - |
문제
스윙 애플리케이션을 만들었지 만 다음과 같은 문제가 있습니다.
이벤트 디스패치 스레드에서 "Thread-Main"이라는 Swingworker 스레드를 시작했으며 GUI의 Jlabel 참조를 "스레드 메인"으로 전달했습니다.
이제 "스레드 메인"에서 10 개의 스레드를 시작했습니다.
이제 10 개의 스레드가 모두 jlabel을 업데이트해야합니다.
어떻게 할 수 있습니까?
누군가 스윙 워크 사람의 10 스레드 하위 클래스를 먼저 만들고 게시 ( "") 메소드를 호출하고 그 "게시"메소드에서 문자열을 전달한 다음 다음 메소드로 게시 된 모든 문자열을 수집 하여이 작업을 수행 할 수 있다고 말했습니다. "스레드 메인"
@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
코드는 그것이 a를 사용한다는 것을 알 수 있습니다ThreadPoolExecutor
내부적으로 최대 10 개의 스레드를 포함합니다. 당신이 여러 가지를 시작하면SwingWorker
동시에 그들은 모두이 집행자를 사용하여 달릴 것입니다. 하지만, 배경 스레드가 병렬로 실행되는지에 대한 직접적인 제어가 없습니다.. - 지점 1을 참조하십시오.
권장 솔루션은 다음과 같습니다.
- 싱글을 만듭니다
SwingWorker
. - 내
doInBackground()
메소드는 직접 또는 사용하여 10 스레드를 시작합니다.ExecutorService
. - a
CountDownLatch
또는CompletionService
마스터 스레드 사이를 동기화하려면 (즉SwingWorker
배경 스레드) 및 작업자 실.
- 싱글을 만듭니다
예시
호출 할 작업자 스레드 수를 정의하고 업데이트 할 jlabel을 선언합니다.
final int nThreads = 10;
JLabel myLbl = new JLabel();
우리가 실행하려는 작업 단위를 Callable<String>
. 그만큼 String
결과를 업데이트하는 데 사용됩니다 JLabel
.
private static class MyCallable implements Callable<String> {
public String call() { ... }
}
이제 시작 a SwingWorker
, 이로 인해 여러 병렬 작업자가 꺼져 모든 처리를 수행 할 것입니다. 근로자는 전화를 통해 결과를 반환하지 않습니다. done()
(따라서 Void
유형) 그러나 ~ 할 것이다 마샬 중간체 String
호출하여 스윙 스레드에 다시 결과를 얻습니다 process(String... chunks)
.
new SwingWorker<Void, String>() {
// See method definitions below.
}.execute();
정의하다 doInBackground()
작업자 스레드를 시작하고 각 결과를 사용하여 CompletionService
.
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);
}
}
이제 우리는 구현합니다 process(String... chunks)
간단히 업데이트하려면 JLabel
전화 할 때.
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]);
}
}
마침내 우리는 무시합니다 done()
스윙 스레드에 대한 예외를 마샬링합니다.
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.
}
}
다른 팁
어쩌면 더 쉬운 접근 방식은 SwingUtilities.invokelater (...) 메소드에서 GUI를 업데이트하는 코드를 래핑하는 것일 수 있습니다.
편집 : 라벨을 업데이트 할 때마다 개별 스레드에서 다음과 같습니다.
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
label.setText(...);
}
});
왜 10 개의 스레드가 필요한가요? 하나의 추가 스레드가하지 않는 이유는 무엇입니까?
실제 질문 : 해결하려는 문제는 무엇입니까?
직접 질문에 답하기 위해 :
1) 예, 올바른 접근법입니다.
3) EDT에서 별도의 스레드를 원한다면; 그런 다음 SwingWorker를 사용해야합니다. 그래서 그것은입니다 방법 그것을하기 위해.
행운을 빕니다!