2017-01-08 70 views
0

这是一个“猜测10秒游戏中的数字”,循环直到用户希望程序停止(游戏无限期重新开始,或直到用户键入“退出”)。 问题是,如果一回合失败(超时),然后用户打印正确的答案,游戏说它赢了。这意味着,最后一场比赛(回合)的线程没有中断。下面是类:如何完全停止包含缓冲读取器的线程?

import java.util.Random; 
import java.util.logging.Level; 
import java.util.logging.Logger; 
import static java.lang.Math.abs; 
import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 


public class Initializer extends Thread { 

    @Override 
    public void run() { 

     Random rand = new Random(); 
     int x = (abs(rand.nextInt())) % 101; 
     GameEngine gameEngine = new GameEngine(x); 
     gameEngine.start(); 
     synchronized (this) { 
      try { 
       wait(10000); 
      } catch (InterruptedException ex) { 
       Logger.getLogger(Initializer.class.getName()).log(Level.SEVERE, null, ex); 
      } 
     } 
     if (gameEngine.isAlive()) { 
      gameEngine.interrupt(); 
      System.out.println("Time is up! The number was " + x); 
     } 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException ex) { 
      Logger.getLogger(Initializer.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 
} 



public class GameEngine extends Thread { 

    private final int valueToGuess; 

    public GameEngine(int x) { 
     this.valueToGuess = x; 
    } 

    @Override 
    @SuppressWarnings("ConvertToTryWithResources") 
    public synchronized void run() { 
     BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
     while (true) { 
      try { 
       int x; 
       String line = br.readLine(); 
       if(line.equalsIgnoreCase("exit")) System.exit(0); 
       try { 
        x = Integer.parseInt(line); 
       } catch (NumberFormatException e) { 
        System.out.println("Please enter a number!"); 
        continue; 
       } 
       if (x < valueToGuess) { 
        System.out.println("Too low!"); 
       } 
       if (x > valueToGuess) { 
        System.out.println("Too high!!"); 
       } 
       if (x == valueToGuess) { 
        System.out.println("You won!"); 
        break; 
       } 
      } catch (IOException ex) { 
       Logger.getLogger(GameEngine.class.getName()).log(Level.SEVERE, null, ex); 
      } 
     } 
     try { 
      br.close(); 
     } catch (IOException ex) { 
      Logger.getLogger(GameEngine.class.getName()).log(Level.SEVERE, null, ex); 
     } 
     this.notifyAll(); 
    } 
} 


public class Main { 

    public static void main(String[] args) throws InterruptedException { 
     System.out.println("Guess the number!"); 
     Initializer counter = new Initializer(); 
     counter.start(); 
     while (true) { 
      if(counter.isAlive()) continue; 
      System.out.println("Game starts in:"); 
      Thread.sleep(1000); 
      System.out.println("5"); 
      Thread.sleep(1000); 
      System.out.println("4"); 
      Thread.sleep(1000); 
      System.out.println("3"); 
      Thread.sleep(1000); 
      System.out.println("2"); 
      Thread.sleep(1000); 
      System.out.println("1"); 
      Thread.sleep(1000); 
      System.out.println("Guess the number!"); 
      counter = new Initializer(); 
      counter.start(); 
     } 
    } 
} 

所以,如果我跑了,考虑到价值猜测是50,和我不圆,当在下一轮开始,如果我输入50,我得到“你赢了! “从上一轮开始,然后从当前回合出现“错误”。我选择了BufferedReader而不是Scanner,因为我读了Scanner.nextLine()阻塞了线程,使其不会中断,而BufferedReader不会阻塞它。问题必须是当我通知初始化程序线程中断GameEngine线程时。我是多线程新手,因此它必须是所有同步或等待/通知指令的错误。请帮忙吗?

+0

不应该是'gameEngine.wait(10000);'?您正在等待并通知不同的对象... – 1000ml

+0

初始化程序应等待10秒钟,如果GameEngine获胜,则通知GameEngine,否则,在这10秒钟后,初始化程序应中断GameEngine。所以我猜不,Initializer等待10秒钟才会收到通知,否则会停止GameEngine。 – Andrew

+0

'this.notifyAll();'这不会通知任何事情,因为GameEngine对象上没有任何东西在等待。 – 1000ml

回答

1

程序不能按照您期望的方式工作,主要是因为您了解Thread.interupt的工作原理。考虑下面的代码两行:

if (gameEngine.isAlive()) { 
     gameEngine.interrupt(); 
     System.out.println("Time is up! The number was " + x); 
    } 

你意料的是,仅仅通过调用interupt,你将被暂停GameEngine线程上interrupt被调用。这不是真的。方法interrupt只会将Thread的状态更新为中断状态。然后,InterruptedException将通过监视Thread的中断状态的任何方法抛出。

由于您不在GameEngine线程中调用任何此类方法,因此InterruptedException永远不会被抛出。尝试添加一个监视run方法GameEngine中的中断状态(例如sleep)的方法,并且您将得到想要的效果,即InterruptedException被抛出。

旁注:始终喜欢构图而不是继承。除非要修改Thread类执行某些功能的方式,否则不要从Thread扩展。改为执行Runnable

+0

我应该在哪里添加这样的方法,以免干扰代码?我有一个无限循环,在那里我经常从键盘读取,不会睡觉()基本上暂停它? – Andrew

+0

@Andrew您对“中断”方法的理解存在缺陷,这会导致您以这种方式设计您的代码。您的代码的方式,它将需要一些调整,不适合在评论部分,也会让你的问题容易被封闭为*太宽* – CKing

+0

我明白了。感谢您的时间。要读更多关于这个问题。 – Andrew

1

它不工作的主要问题是您对Thread.interrupt方法的错误理解。所以,我们来谈谈它。

当你在任何线程对象上调用interrupt时,一个内部标志被称为中断状态被设置。你可以阅读更多here。所以当你打电话给gameEngine.interrupt();时,中断状态标志被置位,但你没有检查代码中任何地方的中断标志的状态。所以你的线程就像通常那样执行。

现在,当它达到String line = br.readLine();一行。它等待用户输入,当用户输入刚刚显示的正确答案时,它与if (x == valueToGuess)条件匹配,并且由于break内部条件,您的循环终止。

所以你需要的是一种方法来检查中断标志是否设置,你可以检查使用Thread.interrupted()thread.isInterrupted()方法。

这两种方法的主要区别在于,Thread.interrupted()方法是静态方法,默认情况下它检查当前线程,它将清除状态标志interrupt。而thread.isInterrupted()是一个实例方法,可以在线程实例上调用,以检查它是否被中断。它不会清除interrupt状态标志。

现在,解决您的问题。 String line = br.readLine();是您的代码中唯一的阻止呼叫。因此,在它后面添加以下行。

所以这会是这样的

String line = br.readLine(); 
// This will check the interrupt status flag and if set will clear it. So next call will return false. 
if (Thread.interrupted()) { 
    break; 
} 

String line = br.readLine(); 
// This will check the interrupt status flag and will not clear it, so any following call will also return true. 
if (this.isInterrupted()) { // or just if (isInterrupted()) as you are extending the Thread class. 
    break; 
} 

希望它能帮助。