如何完全停止包含缓冲读卡器的线程?

时间:2017-01-08 13:21:17

标签: java multithreading

这是“猜测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 over Scanner,因为我读到Scanner.nextLine()阻塞线程,使其不可中断,而BufferedReader不阻止它。当我通知初始化程序线程中断GameEngine线程时,问题必定是。我是多线程新手所以所有那些同步或等待/通知指令一定是错误的。有什么帮助吗?

2 个答案:

答案 0 :(得分:1)

该程序的运行方式与您预期的方式不同主要是因为您了解Thread.interupt的工作原理。请考虑以下两行代码:

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

您的期望是,仅通过调用interupt,您将暂停调用GameEngine的{​​{1}}主题。这不是真的。 interrupt方法仅在中断时更新interrupt的状态。然后,任何监视Thread的中断状态的方法都会抛出InterruptedException

由于您未在Thread主题中调用任何此类方法,因此永远不会抛出GameEngine。尝试在InterruptedException的{​​{1}}方法中添加一个监视中断状态的方法(例如sleep),您将获得所谓的run被抛出的效果。< / p>

旁注:始终更喜欢合成而不是继承。除非您想要修改GameEngine类执行某些功能的方式,否则不要从InterruptedException扩展。而是实施Thread

答案 1 :(得分:1)

它背后的主要问题是你对Thread.interrupt方法的错误理解。所以,让我们谈谈它。

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

现在,当它到达String line = br.readLine();行时。它等待用户输入,当用户输入您刚刚显示的正确答案时,它与if (x == valueToGuess)条件匹配,并且由于{if}条件内的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;
}

希望它有所帮助。