我的任务是在终端中打印货币汇率表,每分钟延迟打印一次表。在用户输入时,我将结束循环并返回海关终端菜单。如何实现这一点?
一般来说,我有一个问题需要弄清楚如何获得无缝的用户体验。我需要在终端中输入用户,如果没有用户操作,我仍然每分钟打印一次数据。最终,如果用户决定终止打印,如何终止整个数据打印循环? -> 运行标志 = ????;
这真的是正确的方法吗?
我已经尝试了几种方法,不幸的是没有工作。
有人可以帮助我吗?
到目前为止,我只有以下代码。循环打印汇率的自定义方法:
private void printExchangeRateTable(final Map<String, Integer> storedExchangeRates){
boolean runFlag = true;
do {
printExchangeRates(storedExchangeRates);
println("Hit any key to stop loop");
Thread threadDelayer = new Thread(new Delayer());
Thread threadListeningUserInput = new Thread(new UserInputListener());
threadDelayer.start();
threadListeningUserInput.start();
runFlag = ????;
} while (runFlag);
}
分层对象
final class Delayer implements Runnable {
public static final long MILISECONDS = 1000L;
@Override
public void run(){
try {
Thread.sleep(60 * MILISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
用户输入侦听器
class UserInputListener implements Runnable {
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
val userInput = scanner.nextLine();
if(userInput != null) {
Thread.currentThread().interrupt();
}
}
}
ScheduledExecutorService
设置重复任务,但在这里,将用户输入的读数发送到后台任务要容易得多
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
CompletableFuture<String> userInput = CompletableFuture.supplyAsync(s::nextLine);
while(!userInput.isDone()) {
printData();
try {
userInput.get(1, TimeUnit.MINUTES);
}
catch(ExecutionException|InterruptedException ex) {} // handled after the loop
catch(TimeoutException ex) {
continue;
}
}
String input = userInput.join();
// handle input
}
private static void printData() {
System.out.println("data");
}
CompletableFuture.supplyAsync(s::nextLine)
会将s.nextLine()
的执行提交到后台任务。然后,循环将一直运行,直到后台任务完成,每分钟执行一次printData()
。这是通过userInput.get(1, TimeUnit.MINUTES)
实现的,如果在指定时间后没有可用的结果,它将抛出TimeoutException
。这里不需要continue
,因为循环无论如何都会继续,但我明确表示。
其他可能的异常是内在处理的。中断不应该发生,但通过进入下一次迭代,它可以用作甚至在时间之前刷新打印数据的信号。当发生ExecutionException
时,当我们在循环后的 join()
语句中获取实际用户输入时,将报告错误状态。
这个循环也可以合并到一个更大的循环中。
Scanner s = new Scanner(System.in);
for(boolean quit = false; !quit; ) {
CompletableFuture<String> userInput = CompletableFuture.supplyAsync(s::nextLine);
while(!userInput.isDone()) {
printData();
try {
userInput.get(1, TimeUnit.MINUTES);
}
catch(ExecutionException|InterruptedException ex) {} // handled implicitly
catch(TimeoutException ex) {
continue;
}
}
String input = userInput.join();
// handle input
if(input.equals("exit")) quit = true;
}
试试这个。我使用相同的逻辑来读取用户输入,但对于打印,我将使用计时器(此处计划每 1 秒运行一次,但您可以更改它(。管理计时器更容易,并且可以从另一个线程取消。
public class Main {
public static void main(String[] args) {
Thread inputListener = new Thread(new UserInputListener());
inputListener.start();
}
static class MyTimerTask extends TimerTask
{
public void run()
{
System.out.println("some output");
}
}
static class UserInputListener implements Runnable {
@Override
public void run() {
Timer timer = new Timer();
TimerTask task = new MyTimerTask();
timer.schedule(task, 0, 1000);
Scanner scanner = new Scanner(System.in);
scanner.nextLine();
timer.cancel();
}
}
}
首先,您需要两个线程,但您不必创建两个线程 - 您可以使用主线程进行用户输入,并在程序开始时启动另一个线程,该线程将每分钟打印一次货币汇率。
每个线程内部都有一个无限(或标志控制的(循环。但是,与其自己管理,我建议使用 ScheduledExecutorService
,否则您将很难中断线程。
下面是一个示例:
import java.util.Scanner;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Example {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleWithFixedDelay(Example::printData, 1, 1, TimeUnit.MINUTES);
Scanner scanner = new Scanner(System.in);
while (true) {
String userInput = scanner.nextLine();
if (userInput.equals("exit")) {
break;
}
}
executor.shutdown();
}
private static void printData() {
System.out.println("data");
}
}
如果没有其他用户输入要处理,并且它仅用于停止后台线程,则根本不需要此线程中的循环,您可以将其简化为:
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleWithFixedDelay(Example::printData, 1, 1, TimeUnit.MINUTES);
Scanner scanner = new Scanner(System.in);
scanner.nextLine();
executor.shutdown();
}