답변:
이를 수행 할 수있는 여러 가지 방법이 있습니다.
아이디어 # 5를 구현하는 방법? 한 가지 방법은 먼저 인터페이스를 만드는 것입니다.
public interface ThreadCompleteListener {
void notifyOfThreadComplete(final Thread thread);
}
그런 다음 다음 클래스를 만듭니다.
public abstract class NotifyingThread extends Thread {
private final Set<ThreadCompleteListener> listeners
= new CopyOnWriteArraySet<ThreadCompleteListener>();
public final void addListener(final ThreadCompleteListener listener) {
listeners.add(listener);
}
public final void removeListener(final ThreadCompleteListener listener) {
listeners.remove(listener);
}
private final void notifyListeners() {
for (ThreadCompleteListener listener : listeners) {
listener.notifyOfThreadComplete(this);
}
}
@Override
public final void run() {
try {
doRun();
} finally {
notifyListeners();
}
}
public abstract void doRun();
}
그런 다음 각 스레드가 확장 NotifyingThread
되고 구현 run()
하는 대신 구현 doRun()
됩니다. 따라서 완료되면 알림을 기다리는 사람에게 자동으로 알립니다.
마지막으로, 모든 스레드를 시작하는 클래스 (또는 적어도 알림을 기다리는 객체)의 기본 클래스에서 implement ThreadCompleteListener
각 스레드를 생성 한 직후와 즉시 해당 클래스를 리스너 목록에 추가하십시오.
NotifyingThread thread1 = new OneOfYourThreads();
thread1.addListener(this); // add ourselves as a listener
thread1.start(); // Start the Thread
그런 다음 각 스레드가 종료되면 notifyOfThreadComplete
방금 완료되었거나 충돌 한 Thread 인스턴스와 함께 메서드가 호출됩니다.
더 잘 참고하는 것 implements Runnable
보다는 extends Thread
위한 NotifyingThread
스레드를 확장하는 것은 일반적으로 새로운 코드에 낙심 될 때. 그러나 나는 당신의 질문에 코딩하고 있습니다. NotifyingThread
구현 하도록 클래스를 변경하면 Runnable
스레드를 관리하는 코드 중 일부를 변경해야합니다. 이는 매우 간단합니다.
notify
메소드 내부가 아니라 메소드 를 호출 할 수 있는지 묻습니다 run
.
CyclicBarrier를 사용한 솔루션
public class Downloader {
private CyclicBarrier barrier;
private final static int NUMBER_OF_DOWNLOADING_THREADS;
private DownloadingThread extends Thread {
private final String url;
public DownloadingThread(String url) {
super();
this.url = url;
}
@Override
public void run() {
barrier.await(); // label1
download(url);
barrier.await(); // label2
}
}
public void startDownload() {
// plus one for the main thread of execution
barrier = new CyclicBarrier(NUMBER_OF_DOWNLOADING_THREADS + 1); // label0
for (int i = 0; i < NUMBER_OF_DOWNLOADING_THREADS; i++) {
new DownloadingThread("http://www.flickr.com/someUser/pic" + i + ".jpg").start();
}
barrier.await(); // label3
displayMessage("Please wait...");
barrier.await(); // label4
displayMessage("Finished");
}
}
label0- 주기적 스레드는 실행 스레드 수에 더한 당사자 수와 기본 실행 스레드 하나 (startDownload ()가 실행되는 스레드 수)로 생성됩니다.
라벨 1 -n 번째 다운로드 스레드가 대기실에 들어갑니다.
라벨 3 -NUMBER_OF_DOWNLOADING_THREADS가 대기실에 입장했습니다. 주요 실행 스레드는 거의 동시에 다운로드 작업을 시작하기 위해 릴리스합니다.
레이블 4- 주요 실행 스레드가 대기실에 들어갑니다. 이것은 이해하기 쉬운 코드 중 가장 까다로운 부분입니다. 대기실에 두 번째 스레드가 들어가는 것은 중요하지 않습니다. 실에 들어가는 스레드가 무엇이든 다른 모든 다운로드 스레드가 다운로드 작업을 완료했는지 확인하는 것이 중요합니다.
라벨 2 -n 번째 다운로드 중 스레드가 다운로드 작업을 마치고 대기실로 들어갑니다. 그것이 메인 실행 스레드를 포함하여 이미 NUMBER_OF_DOWNLOADING_THREADS에 들어간 마지막 스레드 인 경우 메인 스레드는 다른 모든 스레드가 다운로드를 완료 한 경우에만 실행을 계속합니다.
당신은해야 정말 사용하는 솔루션을 선호합니다 java.util.concurrent
. 주제에서 Josh Bloch 및 / 또는 Brian Goetz를 찾아 읽으십시오.
java.util.concurrent.*
쓰레드를 직접 사용 하지 않고 직접 사용할 책임 이 있다면 join()
, 쓰레드가 언제 완료되는지 알아야한다. 다음은 매우 간단한 콜백 메커니즘입니다. 먼저 Runnable
인터페이스를 확장하여 콜백을 갖습니다.
public interface CallbackRunnable extends Runnable {
public void callback();
}
그런 다음 런너 블을 실행할 실행자를 만들고 완료되면 다시 전화하십시오.
public class CallbackExecutor implements Executor {
@Override
public void execute(final Runnable r) {
final Thread runner = new Thread(r);
runner.start();
if ( r instanceof CallbackRunnable ) {
// create a thread to perform the callback
Thread callerbacker = new Thread(new Runnable() {
@Override
public void run() {
try {
// block until the running thread is done
runner.join();
((CallbackRunnable)r).callback();
}
catch ( InterruptedException e ) {
// someone doesn't want us running. ok, maybe we give up.
}
}
});
callerbacker.start();
}
}
}
CallbackRunnable
인터페이스 에 추가해야 할 또 다른 확실한 것은 예외를 처리하는 수단이므로, public void uncaughtException(Throwable e);
거기에 행을 넣고 실행기에 Thread.UncaughtExceptionHandler를 설치하여 해당 인터페이스 메소드로 보내십시오.
그러나 실제로 모든 냄새를 맡기 시작합니다 java.util.concurrent.Callable
. java.util.concurrent
프로젝트에서 허용하는 경우 실제로 사용을 살펴 봐야 합니다.
runner.join()
스레드가 완료되었음을 알고 있기 때문에이 콜백 메커니즘에서 얻는 것, 단순히 호출 한 다음 그 후 원하는 코드 에 대해 명확하지 않습니다 . 해당 코드를 런너 블의 속성으로 정의하는 것만으로 다른 런너 블에 대해 다른 것을 가질 수 있습니까?
runner.join()
기다리는 가장 직접적인 방법입니다. OP가 각 다운로드에 대해 "알림"을 요청했기 때문에 OP가 주요 호출 스레드를 차단하고 싶지 않다고 가정했습니다.이 순서는 완료 될 수 있습니다. 이를 통해 비동기식으로 알림을받을 수 있습니다.
그들이 끝날 때까지 기다리시겠습니까? 그렇다면 Join 메서드를 사용하십시오.
확인하려는 경우 isAlive 속성도 있습니다.
Thread
to start
에 알리지 만 호출은 즉시 반환됩니다. isAlive
간단한 플래그 테스트 여야하지만 Google을 검색했을 때 방법은 native
입니다.
getState ()로 스레드 인스턴스를 조사하여 다음 값 중 하나를 사용하여 Thread.State 열거의 인스턴스를 리턴합니다.
* NEW
A thread that has not yet started is in this state.
* RUNNABLE
A thread executing in the Java virtual machine is in this state.
* BLOCKED
A thread that is blocked waiting for a monitor lock is in this state.
* WAITING
A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
* TIMED_WAITING
A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
* TERMINATED
A thread that has exited is in this state.
그러나 3 명의 자식이 완료되기를 기다리는 마스터 스레드를 갖는 것이 더 나은 디자인이라고 생각하고 다른 3 명이 완료되면 마스터는 계속 실행합니다.
Executors
개체를 사용하여 ExecutorService 스레드 풀 을 만들 수도 있습니다. 그런 다음이 invokeAll
방법을 사용하여 각 스레드를 실행하고 선물을 검색하십시오. 모든 실행이 완료 될 때까지 차단됩니다. 다른 옵션은 풀을 사용하여 각각을 실행 한 다음 awaitTermination
풀 실행이 완료 될 때까지 블록 을 호출 하는 것입니다. shutdown
작업 추가가 완료되면 () 로 전화하십시오 .
멀티 스레딩 프론트에서 지난 6 년 동안 많은 것들이 변경되었습니다.
join()
API 를 사용 하고 잠그는 대신 사용할 수 있습니다.
1. ExecutorService invokeAll()
API
주어진 작업을 실행하고 모든 완료시 상태 및 결과를 보유한 선물리스트를 반환합니다.
하나 이상의 스레드가 다른 스레드에서 수행중인 작업 세트가 완료 될 때까지 대기 할 수 있도록하는 동기화 지원.
CountDownLatch
주어진 카운트로 A 가 초기화됩니다. await 메소드는countDown()
메소드 호출로 인해 현재 카운트가 0에 도달 할 때까지 차단되며 , 그 후에 모든 대기 스레드가 해제되고 이후의 await 호출이 즉시 리턴됩니다. 이것은 일회성 현상이므로 카운트를 재설정 할 수 없습니다. 카운트를 재설정하는 버전이 필요한 경우 CyclicBarrier 사용을 고려하십시오.
3. ForkJoinPool 또는 newWorkStealingPool()
에서 집행 인은 다른 방법입니다
Future
제출에서 모든 작업을 반복 하고 객체 에 대한 ExecutorService
호출 get()
을 차단하여 상태를 확인하십시오.Future
관련 SE 질문을 살펴보십시오.
Thread 클래스 에 대한 javadoc을 보는 것이 좋습니다 .
스레드 조작을위한 여러 메커니즘이 있습니다.
주 스레드 join()
는 세 개의 스레드를 직렬로 연결할 수 있으며, 세 개의 스레드가 모두 완료 될 때까지 진행되지 않습니다.
생성 된 스레드의 스레드 상태를 간격으로여십시오.
별도로 만들어지는 모든 스레드를 넣고 ThreadGroup
및 설문 조사 activeCount()
상의를 ThreadGroup
하고 0에 도착하기를 기다립니다.
스레드 간 통신을위한 사용자 정의 콜백 또는 리스너 유형의 인터페이스를 설정하십시오.
나는 여전히 누락 된 다른 많은 방법이 있다고 확신합니다.
간단하고 짧고 이해하기 쉽고 완벽하게 작동하는 솔루션이 있습니다. 다른 스레드가 끝나면 화면을 그려야했습니다. 메인 스레드가 화면을 제어하기 때문에 할 수 없었습니다. 그래서:
(1) 전역 변수를 만들었습니다 boolean end1 = false;
. 종료시 스레드가 true로 설정합니다. 이것은 "postDelayed"루프에 의해 메인 스레드에서 선택되어 응답됩니다.
(2) 내 스레드에는 다음이 포함됩니다.
void myThread() {
end1 = false;
new CountDownTimer(((60000, 1000) { // milliseconds for onFinish, onTick
public void onFinish()
{
// do stuff here once at end of time.
end1 = true; // signal that the thread has ended.
}
public void onTick(long millisUntilFinished)
{
// do stuff here repeatedly.
}
}.start();
}
(3) 다행히도 "postDelayed"는 메인 스레드에서 실행되므로 매 초마다 다른 스레드를 확인해야합니다. 다른 스레드가 끝나면 다음에 수행하려는 작업을 시작할 수 있습니다.
Handler h1 = new Handler();
private void checkThread() {
h1.postDelayed(new Runnable() {
public void run() {
if (end1)
// resond to the second thread ending here.
else
h1.postDelayed(this, 1000);
}
}, 1000);
}
(4) 마지막으로 다음 코드를 호출하여 코드 어딘가에서 실행하는 모든 것을 시작하십시오.
void startThread()
{
myThread();
checkThread();
}
가장 쉬운 방법은 ThreadPoolExecutor
수업 을 사용하는 것 입니다.
후크 방법
이 클래스는 각 작업 실행 전후에 호출 되는 보호 된 재정의
beforeExecute(java.lang.Thread, java.lang.Runnable)
및afterExecute(java.lang.Runnable, java.lang.Throwable)
메서드를 제공합니다 . 이들은 실행 환경을 조작하는 데 사용될 수 있습니다. 예를 들어 ThreadLocals 다시 초기화, 통계 수집 또는 로그 항목 추가 등이 있습니다. 또한terminated()
Executor가 완전히 종료 된 후에 수행해야하는 특수 처리를 수행하기 위해 메소드 를 대체 할 수 있습니다.
정확히 우리가 필요로하는 것입니다. 우리는 우선합니다 afterExecute()
각 스레드가 수행되며 우선합니다 후 콜백을 얻기 위해 terminated()
모든 스레드가 완료되면 알 수 있습니다.
그래서 여기에 당신이해야 할 일이 있습니다.
실행기를 작성하십시오.
private ThreadPoolExecutor executor;
private int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
private void initExecutor() {
executor = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2, //core pool size
NUMBER_OF_CORES * 2, //max pool size
60L, //keep aive time
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>()
) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
//Yet another thread is finished:
informUiAboutProgress(executor.getCompletedTaskCount(), listOfUrisToProcess.size());
}
}
};
@Override
protected void terminated() {
super.terminated();
informUiThatWeAreDone();
}
}
그리고 실을 시작하십시오 :
private void startTheWork(){
for (Uri uri : listOfUrisToProcess) {
executor.execute(new Runnable() {
@Override
public void run() {
doSomeHeavyWork(uri);
}
});
}
executor.shutdown(); //call it when you won't add jobs anymore
}
내부 메소드 informUiThatWeAreDone();
는 모든 스레드가 완료되면 (예 : UI 업데이트) 필요한 모든 작업을 수행합니다.
참고 :synchronized
작업을 병렬로 수행하고 synchronized
다른 synchronized
방법 에서 메소드 를 호출하기로 결정한 경우 매우 신중해야하므로 메소드 사용을 잊지 마십시오 ! 이것은 종종 교착 상태로 이어집니다
도움이 되었기를 바랍니다!
속성 변경 지원 기능이 내장 된 SwingWorker를 사용할 수도 있습니다. 상태 변경 리스너 예제는 addPropertyChangeListener () 또는 get () 메소드를 참조하십시오 .