我有一个很长的任务,有一个专门的线程,比如说:
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) {}
}
}
好的,现在让我们使用两个线程。案例是:
优雅地说,我的意思是代码必须是可以中断的,但这不是问题的关键。 问题是:在我开始这两个线程之后,我该如何等待“第一个完成”?
public static void main (String [] args) throws InterruptedException {
InputThread it = new InputThread();
it.start();
WorkerThread wt = new WorkerThread();
wt.start();
}
答案 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
完成,并要求ExecutorService
在Monitor
正常结束时终止Worker
。
答案 4 :(得分:0)
回答你的问题:Thread.join()
使当前线程等待引用的线程完成 - 所以在main方法中使用它。
此外,如果您计划使用Thread.interrupt()
进行中断,则还需要在(至少)外部循环中进行状态检查(!isInterrupted()
) - 仅捕获InterruptedException
是不够的,只有当它正在sleep()
等待时它才被抛出 - 它也可能在另一点被打断。