如果在最后 15 秒内 System.in 上没有任何输入,线程将如何超时
How would one time out a thread if there is no input on System.in in the last 15 seconds
我需要让线程监听 system.in,连接所有输入,并且根据某个命令或者如果 10 秒内没有输入,它需要 运行 另一个将使用收集的信息。
需要明确的是,每次输入数据时,我都需要重置 10 秒。
作为并发编程的初学者,我不太确定如何处理这个
欢迎使用 Whosebug!
实现您所要求的一个简单方法是从 System.in
.
schedule a scanning command and wait for the corresponding Future
to return the result or throw an exception while waiting idle for an amount of time. By scanning command I mean a Callable
that will scan the next line
在这种情况下,您将不需要使用手工制作的 Thread
来处理复杂的多线程。只需创建一个合适的 ExecutorService
(通过使用来自 Executors
class 的适当静态方法调用)来调度命令。 ExecutorService
就像 Thread
s 的调度程序,即 Thread
s 的池,它处理它们的生命周期并负责例如创建它们并启动它们。
Future
是一个接口,它的一个实例可以让你监控一个执行任务(比如一个Thread
)的执行时间,即检查是否完成,取消等等。 . Callable
是一个接口,其实现只是 generating/returning computation/method-call 之后的结果,或者抛出 Exception
以防它们无法产生结果。 Future
在我们的上下文中将被 ExecutorService
的调度命令 return 编辑,让我们监控提交的 Callable
s 的生命周期...
我们要提交的 Callable
将只是 return Scanner.nextLine
方法调用的结果。通过向 scehduler 提交 Callable
,我们将返回一个 Future
,它让我们等待 Callable
完成一段给定的时间。要无限期地等待 Callable
的完成,我们使用 get
method. To wait for up to a specific timeout (which is what we are looking for) we use the other get
方法,为其提供我们希望等待的时间量。
我们可以在 Java 8(我正在使用,你可以从链接中看出)及更高版本中创建多种类型的调度程序(即 ExecutorService
s,通过 Executors
helper class(我们也可以通过实例化相应的 classes 来创建它们,但为了简单起见,我们将使用 Executors
的静态方法)。我不是这方面的专家,但一般来说有 固定线程池 ,它最多允许给定数量的 Thread
s 到 运行 在任何给定的time,有scheduled thread pool,可以按时间速率和周期执行Thread
s,有单线程版本其中(即相同的概念,一次只有一个 Thread
),有 缓存线程池 ,它根据需要创建 Thread
并重用现有的已完成线程,最后是 work stealing pool 它的所有线程 block/wait 并行工作(我不确定最后一个,但根据文档可以当您的任务生成其他任务等时很有用)。
由于我们一次提交一个 Callable
(一次一个 Scanner.nextLine
调用),我们可以使用单线程版本。由于我们不关心定期执行提交的 Callable
而是我们希望在每次完成后提交它,那么我们将使用 固定的单线程池 版本。
您也不需要在准备好处理用户输入时启动另一个线程,但您可以使用提交 Callable
的同一个线程。这是以下概念代码中的主线程:
import java.util.LinkedList;
import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class Concept {
public static void main(final String[] args) {
final LinkedList<String> q = new LinkedList<>(); //The collection to hold all user's input.
final Scanner scan = new Scanner(System.in); //The Scanner of the System.in input stream.
final TimeUnit waitUnit = TimeUnit.SECONDS; //What unit of time should we use when waiting for input.
final long waitAmount = 10; //How much time (in 'waitUnit' units) should we wait for.
//An executor with a single (daemon) thread:
final ExecutorService scheduler = Executors.newSingleThreadExecutor(r -> {
final Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
try {
try {
//Main loop for reading and waiting:
for (String input = scheduler.submit(() -> scan.nextLine()).get(waitAmount, waitUnit);
!Objects.equals(input, "stop");
input = scheduler.submit(() -> scan.nextLine()).get(waitAmount, waitUnit))
q.add(input); //Add the user's last input to the collection.
//If this is reached, then the user entered "stop" as input.
System.out.println("Ended by user's input.");
}
catch (final TimeoutException tx) {
//If this is reached, then the time timed out when waiting for user's input.
System.out.println("Ended by timeout.");
}
finally {
//Here you can "consume" however you like all the user's input from the collection:
q.forEach(line -> System.out.println(line)); //I'm just printing all of it.
}
}
catch (final InterruptedException | ExecutionException x) {
x.printStackTrace(); //This is where you handle unexpected exceptions.
}
finally {
//Whatever happened, don't forget to shutdown the ExecutorService:
scheduler.shutdown();
}
}
}
只需将单词 "stop" 作为输入,主线程将继续处理串联的用户输入。或者,您可以等待 10 秒,然后将抛出一个 TimeoutException
,再次继续处理串联用户的输入。
我正在为 Executors
' 方法调用提供 ThreadFactory
. A ThreadFactory
is simply an interface, implementations of which create Thread
s for the given Runnable
s。 Runnable
又是一个接口,它这次定义了一个执行计算的方法 (run
)。在我们的例子中,这个计算是在 ExecutorService
内部创建的,用于存储我们提交的 Callable
结果的引用,以便 get
方法可以使用 returned Future
,这将使其可供客户端代码使用。这个 ThreadFactory
,我提供给 ExecutorService
,正在创建每个 Thread
成为一个 daemon。 Daemon Thread
s 不会阻止程序终止。当所有非 daemon 线程完成后,程序终止,与其他(daemon)线程是否仍在 运行 无关宁.
因此,这归结为我在创建代码时遇到的问题:如果用户输入因超时而停止,而不是将单词 "stop" 作为输入,这意味着 Callable
我们提交的还没有完成。我们提交的 Callable
正在等待 System.in
的输入。因此该线程将 运行 无限期地,或者直到用户输入某些内容。如果创建的 Thread
不是 守护进程 则程序不会终止。这就是我将其设为 daemon.
的原因
但是,如果在超时后,您想在创建(或不创建)Scanner
对象的情况下继续从 System.in
中读取怎么办?然后,您必须首先维护对最后一个 ExecutorService.submit
方法调用的最后一个 Future
return 的引用。
这就是为什么,我有另一个版本,它将扫描完全传递给另一个名为 TimedCallable
的 wrapper 对象,您应该在每次扫描时使用它。即使在超时或以 "stop" 字结束后,您仍应继续使用它来扫描 System.in
:
import java.util.LinkedList;
import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class Main {
public static class TimedCallable<V> implements Callable<V> {
private final Callable<V> callable;
private final ExecutorService scheduler;
private Future<V> lastFuture;
public TimedCallable(final Callable<V> callable) {
this.callable = Objects.requireNonNull(callable);
scheduler = Executors.newSingleThreadExecutor(r -> {
final Thread t = new Thread(r);
t.setDaemon(true); //Needs to be a daemon in order to let the program end.
return t;
});
lastFuture = null;
}
@Override
public synchronized V call() throws InterruptedException, ExecutionException {
if (lastFuture == null)
try {
return callable.call();
}
catch (final Exception x) {
throw new ExecutionException(x);
}
final V v = lastFuture.get();
lastFuture = null;
return v;
}
public synchronized V call(final TimeUnit timeoutUnit,
final long timeoutAmount) throws TimeoutException, InterruptedException, ExecutionException {
if (lastFuture == null)
lastFuture = scheduler.submit(callable);
final V v = lastFuture.get(timeoutAmount, timeoutUnit); /*If it throws TimeoutException,
then the 'lastFuture' property will not be nulled by the following statement:*/
lastFuture = null;
return v;
}
}
public static void main(final String[] args) {
final LinkedList<String> q = new LinkedList<>(); //The collection to hold all user's input.
final Scanner scan = new Scanner(System.in); //The Scanner of the System.in input stream.
final TimeUnit waitUnit = TimeUnit.SECONDS; //What unit of time should we use when waiting for input.
final long waitAmount = 10; //How much time (in 'waitUnit' units) should we wait for.
//Instantiate the scanner's timed-callable:
final TimedCallable<String> scanNextLine = new TimedCallable<>(() -> scan.nextLine());
try {
try {
//Main loop for reading and waiting:
for (String input = scanNextLine.call(waitUnit, waitAmount); !Objects.equals(input, "stop"); input = scanNextLine.call(waitUnit, waitAmount))
q.add(input); //Add the user's last input to the collection.
//If this is reached, then the user entered "stop" as input.
System.out.println("Ended by user's input.");
}
catch (final TimeoutException tx) {
//If this is reached, then the time timed out when waiting for user's input.
System.out.println("Ended by timeout.");
}
finally {
//Here you can "consume" however you like all the user's input from the collection:
q.forEach(line -> System.out.println(line)); //I'm just printing all of it.
//Keep on using the Scanner via the TimedCallable:
System.out.println("Enter next line:");
System.out.println(scanNextLine.call());
System.out.println("Enter last line:");
System.out.println(scanNextLine.call());
}
}
catch (final InterruptedException | ExecutionException x) {
x.printStackTrace(); //This is where you handle unexpected exceptions.
}
}
}
最后说明:我在两个版本中都假设用户在输入句子时可能会因超时而被打断。例如,如果您将超时设置为 1 秒,那么用户可能没有足够的时间来输入他想要的内容,直到超时到期并打扰他。为了更好地控制输入过程,您最好创建一个 GUI 并注册相应的侦听器对象。
我需要让线程监听 system.in,连接所有输入,并且根据某个命令或者如果 10 秒内没有输入,它需要 运行 另一个将使用收集的信息。 需要明确的是,每次输入数据时,我都需要重置 10 秒。 作为并发编程的初学者,我不太确定如何处理这个
欢迎使用 Whosebug!
实现您所要求的一个简单方法是从 System.in
.
Future
to return the result or throw an exception while waiting idle for an amount of time. By scanning command I mean a Callable
that will scan the next line
在这种情况下,您将不需要使用手工制作的 Thread
来处理复杂的多线程。只需创建一个合适的 ExecutorService
(通过使用来自 Executors
class 的适当静态方法调用)来调度命令。 ExecutorService
就像 Thread
s 的调度程序,即 Thread
s 的池,它处理它们的生命周期并负责例如创建它们并启动它们。
Future
是一个接口,它的一个实例可以让你监控一个执行任务(比如一个Thread
)的执行时间,即检查是否完成,取消等等。 . Callable
是一个接口,其实现只是 generating/returning computation/method-call 之后的结果,或者抛出 Exception
以防它们无法产生结果。 Future
在我们的上下文中将被 ExecutorService
的调度命令 return 编辑,让我们监控提交的 Callable
s 的生命周期...
我们要提交的 Callable
将只是 return Scanner.nextLine
方法调用的结果。通过向 scehduler 提交 Callable
,我们将返回一个 Future
,它让我们等待 Callable
完成一段给定的时间。要无限期地等待 Callable
的完成,我们使用 get
method. To wait for up to a specific timeout (which is what we are looking for) we use the other get
方法,为其提供我们希望等待的时间量。
我们可以在 Java 8(我正在使用,你可以从链接中看出)及更高版本中创建多种类型的调度程序(即 ExecutorService
s,通过 Executors
helper class(我们也可以通过实例化相应的 classes 来创建它们,但为了简单起见,我们将使用 Executors
的静态方法)。我不是这方面的专家,但一般来说有 固定线程池 ,它最多允许给定数量的 Thread
s 到 运行 在任何给定的time,有scheduled thread pool,可以按时间速率和周期执行Thread
s,有单线程版本其中(即相同的概念,一次只有一个 Thread
),有 缓存线程池 ,它根据需要创建 Thread
并重用现有的已完成线程,最后是 work stealing pool 它的所有线程 block/wait 并行工作(我不确定最后一个,但根据文档可以当您的任务生成其他任务等时很有用)。
由于我们一次提交一个 Callable
(一次一个 Scanner.nextLine
调用),我们可以使用单线程版本。由于我们不关心定期执行提交的 Callable
而是我们希望在每次完成后提交它,那么我们将使用 固定的单线程池 版本。
您也不需要在准备好处理用户输入时启动另一个线程,但您可以使用提交 Callable
的同一个线程。这是以下概念代码中的主线程:
import java.util.LinkedList;
import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class Concept {
public static void main(final String[] args) {
final LinkedList<String> q = new LinkedList<>(); //The collection to hold all user's input.
final Scanner scan = new Scanner(System.in); //The Scanner of the System.in input stream.
final TimeUnit waitUnit = TimeUnit.SECONDS; //What unit of time should we use when waiting for input.
final long waitAmount = 10; //How much time (in 'waitUnit' units) should we wait for.
//An executor with a single (daemon) thread:
final ExecutorService scheduler = Executors.newSingleThreadExecutor(r -> {
final Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
try {
try {
//Main loop for reading and waiting:
for (String input = scheduler.submit(() -> scan.nextLine()).get(waitAmount, waitUnit);
!Objects.equals(input, "stop");
input = scheduler.submit(() -> scan.nextLine()).get(waitAmount, waitUnit))
q.add(input); //Add the user's last input to the collection.
//If this is reached, then the user entered "stop" as input.
System.out.println("Ended by user's input.");
}
catch (final TimeoutException tx) {
//If this is reached, then the time timed out when waiting for user's input.
System.out.println("Ended by timeout.");
}
finally {
//Here you can "consume" however you like all the user's input from the collection:
q.forEach(line -> System.out.println(line)); //I'm just printing all of it.
}
}
catch (final InterruptedException | ExecutionException x) {
x.printStackTrace(); //This is where you handle unexpected exceptions.
}
finally {
//Whatever happened, don't forget to shutdown the ExecutorService:
scheduler.shutdown();
}
}
}
只需将单词 "stop" 作为输入,主线程将继续处理串联的用户输入。或者,您可以等待 10 秒,然后将抛出一个 TimeoutException
,再次继续处理串联用户的输入。
我正在为 Executors
' 方法调用提供 ThreadFactory
. A ThreadFactory
is simply an interface, implementations of which create Thread
s for the given Runnable
s。 Runnable
又是一个接口,它这次定义了一个执行计算的方法 (run
)。在我们的例子中,这个计算是在 ExecutorService
内部创建的,用于存储我们提交的 Callable
结果的引用,以便 get
方法可以使用 returned Future
,这将使其可供客户端代码使用。这个 ThreadFactory
,我提供给 ExecutorService
,正在创建每个 Thread
成为一个 daemon。 Daemon Thread
s 不会阻止程序终止。当所有非 daemon 线程完成后,程序终止,与其他(daemon)线程是否仍在 运行 无关宁.
因此,这归结为我在创建代码时遇到的问题:如果用户输入因超时而停止,而不是将单词 "stop" 作为输入,这意味着 Callable
我们提交的还没有完成。我们提交的 Callable
正在等待 System.in
的输入。因此该线程将 运行 无限期地,或者直到用户输入某些内容。如果创建的 Thread
不是 守护进程 则程序不会终止。这就是我将其设为 daemon.
但是,如果在超时后,您想在创建(或不创建)Scanner
对象的情况下继续从 System.in
中读取怎么办?然后,您必须首先维护对最后一个 ExecutorService.submit
方法调用的最后一个 Future
return 的引用。
这就是为什么,我有另一个版本,它将扫描完全传递给另一个名为 TimedCallable
的 wrapper 对象,您应该在每次扫描时使用它。即使在超时或以 "stop" 字结束后,您仍应继续使用它来扫描 System.in
:
import java.util.LinkedList;
import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class Main {
public static class TimedCallable<V> implements Callable<V> {
private final Callable<V> callable;
private final ExecutorService scheduler;
private Future<V> lastFuture;
public TimedCallable(final Callable<V> callable) {
this.callable = Objects.requireNonNull(callable);
scheduler = Executors.newSingleThreadExecutor(r -> {
final Thread t = new Thread(r);
t.setDaemon(true); //Needs to be a daemon in order to let the program end.
return t;
});
lastFuture = null;
}
@Override
public synchronized V call() throws InterruptedException, ExecutionException {
if (lastFuture == null)
try {
return callable.call();
}
catch (final Exception x) {
throw new ExecutionException(x);
}
final V v = lastFuture.get();
lastFuture = null;
return v;
}
public synchronized V call(final TimeUnit timeoutUnit,
final long timeoutAmount) throws TimeoutException, InterruptedException, ExecutionException {
if (lastFuture == null)
lastFuture = scheduler.submit(callable);
final V v = lastFuture.get(timeoutAmount, timeoutUnit); /*If it throws TimeoutException,
then the 'lastFuture' property will not be nulled by the following statement:*/
lastFuture = null;
return v;
}
}
public static void main(final String[] args) {
final LinkedList<String> q = new LinkedList<>(); //The collection to hold all user's input.
final Scanner scan = new Scanner(System.in); //The Scanner of the System.in input stream.
final TimeUnit waitUnit = TimeUnit.SECONDS; //What unit of time should we use when waiting for input.
final long waitAmount = 10; //How much time (in 'waitUnit' units) should we wait for.
//Instantiate the scanner's timed-callable:
final TimedCallable<String> scanNextLine = new TimedCallable<>(() -> scan.nextLine());
try {
try {
//Main loop for reading and waiting:
for (String input = scanNextLine.call(waitUnit, waitAmount); !Objects.equals(input, "stop"); input = scanNextLine.call(waitUnit, waitAmount))
q.add(input); //Add the user's last input to the collection.
//If this is reached, then the user entered "stop" as input.
System.out.println("Ended by user's input.");
}
catch (final TimeoutException tx) {
//If this is reached, then the time timed out when waiting for user's input.
System.out.println("Ended by timeout.");
}
finally {
//Here you can "consume" however you like all the user's input from the collection:
q.forEach(line -> System.out.println(line)); //I'm just printing all of it.
//Keep on using the Scanner via the TimedCallable:
System.out.println("Enter next line:");
System.out.println(scanNextLine.call());
System.out.println("Enter last line:");
System.out.println(scanNextLine.call());
}
}
catch (final InterruptedException | ExecutionException x) {
x.printStackTrace(); //This is where you handle unexpected exceptions.
}
}
}
最后说明:我在两个版本中都假设用户在输入句子时可能会因超时而被打断。例如,如果您将超时设置为 1 秒,那么用户可能没有足够的时间来输入他想要的内容,直到超时到期并打扰他。为了更好地控制输入过程,您最好创建一个 GUI 并注册相应的侦听器对象。