两个java线程,第一个完成应该中断另一个

时间:2013-06-02 16:29:14

标签: java multithreading concurrency

我有一个很长的任务,有一个专门的线程,比如说:

public static class WorkerThread extends Thread{

    @Override public void run () {
        for (int i = 1; i<=10; i++) {
            System.out.println("worker thread progress: " + i + "/10");
            try{Thread.sleep(1000);} catch (InterruptedException ignore) {}
        }

    System.out.println("the worker thread HAS FINISHED!");

    }

}

在此任务期间,我想听取用户取消长任务的命令行。 由于System.in的特殊性,代码如下(即如果你想要一个可中断的控制台读取,你被迫使用轮询):

public static class InputThread extends Thread {

    @Override public void run() {
       try{
           StringBuilder sb = new StringBuilder(); 

           do {
                while (System.in.available()==0) { Thread.sleep(200); }

                sb.append((char)System.in.read());

           } while (!sb.toString().equals("cancel\n"));

           System.out.println("the user-input thread HAS FINISHED!");

       } catch (IOException ignored) {} catch (InterruptedException ie) {}
    }

}

好的,现在让我们使用两个线程。案例是:

  • WorkerThread在InputThread之前完成。在这种情况下,我必须(优雅地)中断InputThread,因为没有更多用户取消线程的可能性
  • InputThread在WorkerThread之前完成。用户输入了“取消”命令,因此我必须(优雅地)中断WorkerThread

优雅地说,我的意思是代码必须是可以中断的,但这不是问题的关键。 问题是:在我开始这两个线程之后,我该如何等待“第一个完成”?

public static void main (String [] args) throws InterruptedException {

    InputThread it = new InputThread();

    it.start();

    WorkerThread wt = new WorkerThread();

    wt.start();


}

5 个答案:

答案 0 :(得分:2)

您的用例的首选技术是CountdownLatch。两个线程都必须调用countdown作为他们做的最后一件事,并在你调用await的主线程上。

答案 1 :(得分:1)

使用循环进行轮询(假设您的类为T04):

public static void main (String [] args) throws InterruptedException {  

    InputThread it = new InputThread();
    it.start();

    WorkerThread wt = new WorkerThread();
    wt.start();

    while(true) {           //I think this works as polling
       if(it.isFinished()) {
           wt.finish();
           return;
       } 
       else if(wt.isFinished()) {
           it.finish();
           return;
       }
       try{Thread.sleep(50);} catch (InterruptedException ignore) {}
    }
}

并将此代码添加到InputThread类(您可以执行超类以重用代码):

public class InputThread extends Thread {
private boolean finished = false;

@Override 
public void run() {
   try{
       StringBuilder sb = new StringBuilder(); 

       do {
            while (!this.finished && System.in.available()==0) { Thread.sleep(200); }

            if(this.finished) 
                return;
            else
                sb.append((char)System.in.read());

       } while (!sb.toString().equals("cancel\n") && !this.finished);                   

       System.out.println("the user-input thread HAS FINISHED!");

       this.finished = true;

   } catch (IOException ignored) {} catch (InterruptedException ie) {}
}

public boolean isFinished() {
    return this.finished;
}

public void finish(){
    this.finished = true;
}

}

您的WorkerThread可能看起来像:

public class WorkerThread extends Thread{
public boolean finished = false;

@Override 
public void run () {
    for (int i = 1; i<=10; i++) {
        if(this.finished)
            return;
        System.out.println("worker thread progress: " + i + "/10");
        try{Thread.sleep(1000);} catch (InterruptedException ignore) {}
    }

    System.out.println("the worker thread HAS FINISHED!");
    this.finished = true;
}

public boolean isFinished() {
    return this.finished;
}

public void finish(){
    this.finished = true;
}
}

答案 2 :(得分:1)

执行此操作的一种方法是使用ExecutorService,然后跟踪两个runnable的Future的状态。这是一个小例子:

ExecutorService es = Executors.newFixedThreadPool(2); // InputThread and WorkerThread
try {
    Future<?> workerFuture = es.submit(workerThread);
    Future<?> inputFuture = es.submit(inputThread);

    while(!inputFuture.isDone() && !workerFuture.isDone()) {
        // Sleep and check status again until one of the thread is complete
    }

    if(inputFuture.isDone()) { // User inputs "cancel", so cancel worker thread
        workerFuture.cancel();
    } else { // Worker thread is complete, cancel the input thread
        inputFuture.cancel();
    }

} finally {
    es.shutdown();
}

答案 3 :(得分:1)

您可以使用两个带ExecutorService的线程和中断进行通信:

private static class Worker implements Runnable {

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println("worker thread progress: " + i + "/10");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                System.out.println("We have been cancelled");
                return;
            }
        }
    }
}

private static class Monitor implements Runnable {

    private final Future<?> workerFuture;

    public Monitor(Future<?> workerFuture) {
        this.workerFuture = workerFuture;
    }

    @Override
    public void run() {
        try (final BufferedReader br =
                new BufferedReader(new InputStreamReader(System.in))) {
            while (true) {
                if (br.ready()
                        && "cancel".equals(br.readLine())) {
                    System.out.println("Input is cancel, kill worker.");
                    workerFuture.cancel(true);
                    return;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    System.out.println("Mointor cancelled. Stop.");
                    return;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

public static void main(String[] args) throws Exception {
    final ExecutorService executorService = Executors.newCachedThreadPool();
    final Future<?> longTask = executorService.submit(new Worker());
    final Future<?> monitor = executorService.submit(new Monitor(longTask));
    //wait for long task to complete
    try {
        longTask.get();
        monitor.cancel(true);
        System.out.println("Main task finished normally.");
    } catch (CancellationException ex) {
        System.out.println("Main task killed.");
    }
    executorService.shutdown();
    executorService.awaitTermination(1, TimeUnit.DAYS);
}

Worker工作中断并在完成或中断时退出。

Monitor中断读取,如果显示“取消”则要求ExecutorService通过其Worker中断Future

主线程等待Worker通过其Future完成,并要求ExecutorServiceMonitor正常结束时终止Worker

答案 4 :(得分:0)

回答你的问题:Thread.join()使当前线程等待引用的线程完成 - 所以在main方法中使用它。

此外,如果您计划使用Thread.interrupt()进行中断,则还需要在(至少)外部循环中进行状态检查(!isInterrupted()) - 仅捕获InterruptedException是不够的,只有当它正在sleep()等待时它才被抛出 - 它也可能在另一点被打断。