0

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

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();


}
4

5 回答 5

2

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

于 2013-06-02T16:49:06.433 回答
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;
}
}
于 2013-06-02T17:08:00.677 回答
1

一种方法是使用ExecutorService然后跟踪Future两个可运行的 s 的状态。这是一个小例子:

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();
}
于 2013-06-02T17:14:45.617 回答
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中断。WorkerFuture

主线程等待Worker通过其完成Future并要求终止ExecutorService如果Monitor正常Worker完成。

于 2013-06-02T17:14:54.213 回答
0

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

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

于 2013-06-02T16:33:50.127 回答