Thread中的阻塞方法

时间:2014-05-12 06:39:04

标签: java multithreading

我正在尝试为国际象棋游戏开发计时器。 应该有像

这样的功能

等待用户输入从控制台

移动

如果等待时间> 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行读取输入。 有什么方法可以解决这个问题吗?

3 个答案:

答案 0 :(得分:2)

已经有Timer个班级(实际上有两个班级,java.util.Timerjavax.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();
    }
}