我正在尝试为国际象棋游戏开发计时器。 应该有像
这样的功能等待用户输入从控制台
移动如果等待时间> 60
停止等待并继续前进。
我能想到解决这个问题的方法是使用Thread:
public class Game {
Thread t1 = new PlayDisc(this);
Thread t2 = new Timer(this);
public static void main(String[] args) {
t1.start();
t2.start();
}
}
public class Timer extends Thread{
Game g;
public Timer(Game g) {
this.g = g;
}
@Override
public void run() {
int i = 0;
while(true){
if(i > 6000) {
g.t1.interrupt();
break;
}
}
}
}
public class PlayDisc extends Thread{
private Game g;
public PlayDisc(Game g) {
this.g = g;
}
@Override
public void run() {
Scanner s = new Scanner(System.in);
int x = s.nextInt();
int y = s.nextInt();
Point p = new Point(x, y);
cm.nextPoint = p;
s.close();
}
}
我知道这不会起作用,因为Scanner.nextInt()是一种阻塞方法。但是我需要从cmd行读取输入。 有什么方法可以解决这个问题吗?
答案 0 :(得分:2)
已经有Timer
个班级(实际上有两个班级,java.util.Timer
和javax.swing.Timer
)。但是正如你已经意识到的那样,nextInt()
的阻塞性质会阻止你在超时后做任何事情。您需要一个额外的库,它可以提供比默认情况下Java更好的控制台控制。那,或者使用Swing
。
编辑:通过使用hasNextInt()
的轮询循环,可能会执行某些类型的黑客攻击。这样你就不会让扫描线程阻塞。
重新编辑:不,这是不可能的,因为hasNext()会阻止。你必须测试中断是否会让你摆脱麻烦(可能不会)。
答案 1 :(得分:1)
Scanner
的问题在于,您无法控制方法读取更多字节,这些字节总是会导致阻塞。安全的方法是手动从System.in
读取并创建一个Scanner
,它只能读取您从控制台获得的字节数。然后你可以进行轮询(带睡眠)来实现超时。通过选择合适的睡眠时间,您可以在响应能力和CPU使用率之间取得适当的平衡。
下面的示例程序使用200ms的检查间隔,这足以被人类用户视为“立即响应”。此值与您可以自由配置的等待时间无关(只要它明显高于检查间隔)。
要注意的其他事项是我们在开始时计算截止日期,而不是将等待时间聚合为独立于循环内的CPU使用情况。我们使用System.nanoTime()
独立于可能发生在系统时钟上的更改。
long timeOutNS=TimeUnit.MINUTES.toNanos(1); // 1 min timeout
long checkNS=TimeUnit.MILLISECONDS.toNanos(200); // check input every 200ms
int input=0;
boolean hasInput=false;
readWithTimeOut: {
System.out.println("Enter int: ");
long deadLine=System.nanoTime() + timeOutNS;
for(;;) {
int a = System.in.available();
if(a>0) {
byte[] b=new byte[a];
a=System.in.read(b);
if(a<=0) break readWithTimeOut;
Scanner scanner=new Scanner(new ByteArrayInputStream(b, 0, a));
if(scanner.hasNextInt()) {
input=scanner.nextInt();
hasInput=true;
break;
}
else if(scanner.hasNext())
System.err.println("not an int: "+scanner.next()); // consumes token
continue;
}
long remaining=deadLine-System.nanoTime();
if(remaining<=0) {
System.err.println("timeout");
break readWithTimeOut;
}
LockSupport.parkNanos(Math.min(remaining, checkNS));
}
}
System.out.println( hasInput? "entered "+input: "no valid input" );
答案 2 :(得分:1)
InputStream.available()
是一种非阻止方法,可用于检查流中是否存在某些内容。
如果你不关心旋转(从而浪费处理器核心),它就像它可以获得的那样简单:
import java.io.IOException;
import java.util.Scanner;
public class ConsoleReadWithTimeout {
static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) throws Exception {
int i = readIntWithTimeout(5 * 1000);
scanner.close();
}
// returns -1 in case of timeout
static private int readIntWithTimeout(long timeoutInMs) throws IOException {
long startTime = System.currentTimeMillis();
while (System.in.available() == 0) {
if (timeoutInMs < System.currentTimeMillis() - startTime) {
return -1; // or maybe throw a TimeoutException ?
}
}
return scanner.nextInt();
}
}