很快,我需要创建一个具有客户机/服务器架构的Java应用程序(客户端表示某种数学函数,当它们完成计算时,服务器将使用这些值并产生最终结果)。另外,应该有两种类型的取消,第一种允许通过输入符号来停止服务器,第二种允许通过三个选项显示周期性提示符(继续,不带提示,取消)。当用户看到提示符时,计算结果不应该干预控制台。您可以在这里看到完整的描述,名为实验室1
使用客户端生成的值(在这里我使用CompletionService)
private void runFuturesConsumer() {
new Thread(() -> {
try {
while (true) {
Future<Integer> consumedFuture = completionService.take();
Integer consumedValue = consumedFuture.get();
if (Objects.equals(consumedValue, INTERRUPTION_VALUE)) {
break; // clients were interrupted, stop futures consuming
} // happens after cancelServerFuture procedure call
valuesContainer.add(consumedValue);
if (consumedValue == SHORT_CIRCUIT_CONDITION) {
cancelServerFuture();
transferResult(true, null);
break;
}
if (valuesContainer.size() == clientsNumber) {
transferResult(false, null);
break;
}
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}).start();
}将结果从服务器传输到ApplicationManager (处理输出/输入和取消任务的类)
private void transferResult(boolean isShortCircuited, String failureReport) {
// simple lock/sync will produce a deadlock (if computations were completed during the prompt)
if (outputLock.tryLock()) {
try {
if (failureReport != null) {
serverListener.onFailureReported(failureReport);
} else if (isShortCircuited) {
serverListener.onCompletedComputation(SHORT_CIRCUIT_CONDITION, true);
} else {
serverListener.onCompletedComputation(valuesContainer.stream()
.reduce(1, (accumulator, elem) -> accumulator * elem), false);
}
} finally {
cancelServerFuture();
outputLock.unlock();
}
}
}将在用户选择停止执行时调用。
void stopServer() {
if (outputLock.tryLock()) {
transferResult(false, "stopped before the completion");
}
}此守护进程等待某人输入"q“,然后停止服务器。
private void runKeyPressDaemon() {
Thread thread = new Thread(() -> {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String message = null;
while (true) {
try {
message = bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if (message != null && message.equals("q")) {
// it's definitely bad if scheduler decides to reschedule here
// because output lock happens only inside stop server method
// (trying to lock right after readLine procedure creates possibility of losing the result)
server.stopServer();
break;
}
}
});
thread.setDaemon(true);
thread.start();
}处理第二类取消(提示)
private void runPromptScheduler() {
scheduledExecutor.scheduleWithFixedDelay(this::displayPrompt, INITIAL_DELAY, SCHEDULER_PERIOD, TimeUnit.SECONDS);
}
private void displayPrompt() {
promptLock.lock();
try {
Scanner scanner = new Scanner(System.in);
System.out.print("continue(1), continue without prompt(2), cancel(3): ");
int readValue = scanner.nextInt();
switch (readValue) {
case 1:
handlePrompt(PromptOptions.CONTINUE);
break;
case 2:
handlePrompt(PromptOptions.CONTINUE_WITHOUT_PROMPT);
break;
case 3:
handlePrompt(PromptOptions.CANCEL);
break;
}
} finally {
promptLock.unlock();
}
}
private void handlePrompt(PromptOptions promptOptions) {
switch (promptOptions) {
case CONTINUE:
break;
case CONTINUE_WITHOUT_PROMPT:
scheduledExecutor.shutdownNow();
break;
case CANCEL:
server.stopServer();
break;
}
}响应结果输出
@Override
public void onCompletedComputation(int result, boolean isShortCircuited) {
promptLock.lock();
try {
if (isShortCircuited) {
System.out.println("result (short-circuit): " + result);
} else {
System.out.println("result: " + result);
}
} finally {
scheduledExecutor.shutdownNow();
promptLock.unlock();
}
}
@Override
public void onFailureReported(String cause) {
promptLock.lock();
try {
System.out.println("failure caused by: " + cause);
} finally {
scheduledExecutor.shutdownNow();
promptLock.unlock();
}
}在我的解决方案中,我不喜欢的是存在某种种族条件,因为不清楚谁将获得“想要的”outputLock。它可以是取消任务(输入符号或提示符)或FuturesConsumer (如果它消耗了所有的期货(例如,查看来自keyPressDaemon方法的注释,用户可以输入"q",但是FuturesConsumer可以占用CPU时间并打印结果)。我可以将这个outputLock移动到FuturesConsumer方法,但它不会解决问题。另外,我认为通过使用锁而不是最好的解决方案来禁止结果输出(当有活动的提示符时)。提前感谢您的建议和帮助。
发布于 2016-10-02 11:50:22
一些我注意到的小问题(我应该停止用这样的句子来开始我的答案):
ExecutorService和相关的类。有趣的是你已经这么做了.runFuturesConsumer和runKeyPressDaemon时,您总是启动线程。这很糟糕,因为它在任何地方都没有文档记录,我希望该方法能够跟踪您试图运行的内容是否已经在运行,并且在不需要时不会启动另一个实例。cancelServerFuture的代码,但是命名表明了一些令人不安的东西。取消通常意味着没有结果,但您的代码暗示应该有?也许吧?,我不认为取消服务器计算,然后将一些东西传输给服务器(短路就是这样做的)。或者我只是遗漏了什么..。"failure caused by: stopped before the completion"。这并不是预期的行为。相反,我希望要么没有消息,要么什么会产生"Successfully cancelled execution"的效果。POISON_PILL。至少在我听到的时候,这是传统的名字。当我们处于中断状态时:通过Object#equals比较INTERRUPTION_VALUE和通过引用等于(==)比较其他情况是很有趣的。我不太清楚这是为什么,但唉.handlePrompt (并且在那里有效地嵌套一个开关块),您可以在那里“内联”执行。附加注意:“不正确”的用户输入没有在那里处理。传入"asd“只会用InputMismatchException来炸掉代码:(好吧,这应该涵盖大部分内容,我强烈建议进行后续审查,尽管如此:)
https://codereview.stackexchange.com/questions/142810
复制相似问题