【问题标题】:main thread not getting interrupted主线程没有被中断
【发布时间】:2012-09-29 23:35:02
【问题描述】:

我写了一个代码,如果被测试的数字是快乐的数字,则打印 1,否则打印 0。

class Ishappy extends Thread {
    private Integer num;
    private Thread main;
    private volatile boolean out = false;

    Ishappy(int i, Thread main) {
        this.main = main;
        num = i;
    }

    void Exit() {
        out = true;
    }

    @Override
    public void run() {
        while(!out && num != 1) {
            if(num == 1) {
                main.interrupt();
                break;
            }

            String s = num.toString();
            int temp = 0;
            for(int i = 0 ; i < s.length(); i++) {
                int x = Integer.parseInt(s.substring(i, i+1));
                temp += x*x;
            }
            num = temp;
        }
    }
}

public class Happy_numbers {
    public static void main(String[] args) {
        byte path[] = null;

        String s = "d:\\data.txt";

        try(FileInputStream fin = new FileInputStream(s)) {
            InputStreamReader in = new InputStreamReader(fin);
            BufferedReader br = new BufferedReader(in);
            s = br.readLine();
            int num;
            while(s != null) {
                num = Integer.parseInt(s);

                Ishappy ishappy = new Ishappy(num,Thread.currentThread()); 
                ishappy.start();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    System.out.println(1);
                    continue;
                }
                if(ishappy.isAlive()) {
                    ishappy.Exit();
                    System.out.println(0);
                } else
                    System.out.println(11);

                s = br.readLine();
            }
        } catch (FileNotFoundException ex) {
            System.out.println("File not found.");
        }catch(IOException ex){
        }
    }
}

但是上面的代码总是打印 11 作为一个快乐的数字,这意味着main 永远不会被打断。怎么了?

data.txt的内容是

1
7
22

其中 1 和 7 是快乐的数字,而 22 则不是。

【问题讨论】:

  • 您的代码在命名类和方法的方式上违反了公认的 Java 编码标准。类名不应嵌入'_' 字符,应使用“驼峰式”,方法名不应以大写字母开头。
  • 我已经对我的代码进行了更改。那个大写的“e”确实看起来有点冒犯。我只是使用'-',因为这是我被要求保留文件名的原因。 :)

标签: java multithreading


【解决方案1】:

当我阅读你的程序时,第一次通过主循环,一个新的IsHappy 线程将被分叉,num 设置为1。在IsHappy.run() 方法中,如果num1,它会立即退出。

while(!out && num != 1)

main 将休眠,然后打印11,因为线程不再运行。

if(ishappy.isAlive()) {
    ishappy.Exit();
    System.out.println(0);
} else
   System.out.println(11);

这不是你所期望的吗?我认为您应该学习如何使用调试器。这是一个很好的教程,教你如何debug your program in Eclipse

如果处理了数字7,则IsHappy.run() 方法将旋转,一遍又一遍地将num 设置为49 (7*7)。然后main 会看到IsHappy 还活着,并会调用IsHappy.Exit() 并打印0

如果处理了数字21,则IsHappy.run() 方法将旋转,一遍又一遍地将num 设置为5 (2*2+1*1)。 main 将再次Exit() 线程并打印0

所以我猜你的输出是:

11
0
0

main 线程永远不会被中断,因为while 循环将阻止它运行如果num == 1

关于您的代码的其他几个 cmets。这可能是测试代码,所以没关系,但要学究:

  • 永远不要捕获并丢弃异常(请参阅您的 IOException 捕获)。至少在一个空的 catch 块中添加注释,说明为什么你不关心异常。
  • 您的线程正在旋转进行计算。这显然不是对资源的良好利用。
  • 您没有关闭任何输入流或阅读器。在处理这些问题时,请始终使用 try/finally。
  • Exit() 方法应该以小写字母开头。该方法的更好名称是stop

【讨论】:

  • 你对代码的 cmets 是正确的......但我认为在某些情况下它甚至在测试代码中也很重要! 1)如果即使在测试代码中也出现了意想不到的问题,OP应该关心它,如果他希望测试产生有意义的答案。 4) 他 >>is
  • 好吧,我没有为异常烦恼,因为我是新手,我只是想让事情变得简单。不,线程不仅仅是在旋转进行计算,事实上代码的输出是11 11 0。是的,我可以在 Thread 类中重载 exit() 方法,但我不能用 stop() 来做到这一点,它在 Thread 类中被声明为 final 并且不能再被覆盖。我只是不明白为什么 main() 线程没有被中断。如果可以,请帮助我。
  • 另外请告诉我如何调试多线程代码,因为调试器从不进入线程代码。顺便说一句,我正在使用 NetBeans。
  • @user1232138 你的线程代码旋转了一秒钟,一遍又一遍地计算结果,直到主线程调用Exit()。这真的是你想要的吗?如果输出是11 11 0,那么我猜你已经溢出了整数算术并且num 滚动为1。我对此感到惊讶。
  • @user1232138 调试应该与特定线程在断点处停止的线程一起工作。我不确定 netbeans 会如何使事情复杂化。你可以直接对你的代码进行单元测试和调试吗?
【解决方案2】:

我认为这里不需要线程,如果数字的平方和等于 1,那么快乐的数字就是快乐的。如果序列包含一个已经测试过的数字。就终止吧。

来自维基百科

如果 n 不快乐,那么它的序列不会变为 1。相反,它会进入循环。

public class Happy_numbers {        
    static int[]SQUARES={0,1,4,9,16,25,36,49,64,81};
    public static boolean is_happy(int n){
        return is_happy(n, new HashSet<Integer>());
    }
    public static boolean is_happy(int n, Collection<Integer> sofar){
        if(n==1) return true;
        else if(sofar.contains(n)) return false;

        sofar.add(n);

        if(n<10) {
            return is_happy(SQUARES[n], sofar);
        }

        char[]digits=String.format("%s", n).toCharArray();
        int s = 0;
        for(char c:digits){
            s+= SQUARES[Integer.valueOf(String.format("%s", c))];
        }

        return is_happy(s, sofar);
    }
    public static void main(String[]args){
        Collection<Integer> c1 = Arrays.asList(
                1, 7, 10, 13, 19, 23, 28, 31, 32, 44, 49, 68, 70, 79, 82, 86, 91, 94, 97, 
                100, 103, 109, 129, 130, 133, 139, 167, 176, 188, 190, 192, 193, 203, 208, 
                219, 226, 230, 236, 239, 262, 263, 280, 291, 293, 301, 302, 310, 313, 319, 
                320, 326, 329, 331, 338, 356, 362, 365, 367, 368, 376, 379, 383, 386, 391, 
                392, 397, 404, 409, 440, 446, 464, 469, 478, 487, 490, 496 );
        Collection<Integer> c2 = new ArrayList<Integer>(c1.size());
        long t = System.currentTimeMillis();
        int c = 1;
        for(int i=0;i<500;i++){
            if(is_happy(i)) {
                System.out.print(i+", ");
                if(c++ % 20 == 0) System.out.println();
                c2.add(i);
            }

        }
        t = System.currentTimeMillis()-t;
        System.out.println("\nTIME : " + t);
        System.out.println("Got them all < 500 : " + (c2.containsAll(c1) && c1.containsAll(c2)));
    }
}

还可以通过使用地图或任何缓存技术进一步改进它,因此如果一个数字很满意并且您之前已经计算过这个数字,则无需再次执行相同的操作。来自维基百科,

500 以下的快乐数字是: 1、7、10、13、19、23、28、31、32、44、49、68、70、79、82、86、91、94、97、100、103、109、 129、130、133、139、167、176、188、190、192、193、203、208、219、226、230、236、239、262、 263, 280, 291, 293, 301, 302, 310, 313, 319, 320, 326, 329, 331, 338, 356, 362, 365, 367, 368, 376、379、383、386、391、392、397、404、409、440、446、464、469、478、487、490、496 (OEIS 中的序列 A007770)。

而上面的代码需要 435 才能得到所有低于 500 的快乐数字。

1, 7, 10, 13, 19, 23, 28, 31, 32, 44, 49, 68, 70, 79, 82, 86, 91, 94, 97, 100, 
103, 109, 129, 130, 133, 139, 167, 176, 188, 190, 192, 193, 203, 208, 219, 226, 230, 236, 239, 262, 
263, 280, 291, 293, 301, 302, 310, 313, 319, 320, 326, 329, 331, 338, 356, 362, 365, 367, 368, 376, 
379, 383, 386, 391, 392, 397, 404, 409, 440, 446, 464, 469, 478, 487, 490, 496, 
TIME : 435
Got them all < 500 : true

因此,我对您的代码进行了一些更改,以便记住到目前为止计算的内容,至少对于当前数字而言。

public class Happy_numbers {

    static class Ishappy extends Thread {
        private Integer num;
        private Thread main;
        private volatile boolean out = false;

        private boolean unhappy = false;

        Ishappy(int i, Thread main) {
            this.main = main;
            num = i;
        }

        public boolean isUnhappy() {
            return unhappy;
        }

        void Exit() {
            out = true;
        }

        @Override
        public void run() {
            Set<Integer> sofar = new HashSet<Integer>();
            while(!out && num != 1) {
                unhappy = sofar.contains(num);
                if(num == 1 || unhappy) {
                    main.interrupt();
                    break;
                }

                sofar.add(num);

                String s = num.toString();
                int temp = 0;
                for(int i = 0 ; i < s.length(); i++) {
                    int x = Integer.parseInt(s.substring(i, i+1));
                    temp += x*x;
                }
                num = temp;
            }
        }
    }

    public static void main(String[] args) throws Exception{
        byte path[] = null;

        String s = "./data.txt";

        FileInputStream fin = new FileInputStream(s);
        InputStreamReader in = new InputStreamReader(fin);
        BufferedReader br = new BufferedReader(in);
        int num;
        while((s = br.readLine()) != null) {
            num = Integer.parseInt(s);

            Ishappy ishappy = new Ishappy(num,Thread.currentThread()); 
            ishappy.start();
            ishappy.join();
            if(ishappy.isUnhappy()){
                System.out.println("Number ["+num+"] is not happy");
            }else{
                System.out.println("Number ["+num+"] is happy");
            }
        }
        br.close();
        in.close();
        fin.close();
    }
}

输出是

Number [1] is happy
Number [7] is happy
Number [22] is not happy

编辑

我找到了主线程不被中断的原因。

在主 while 循环中,检查 num 是否为 1,如果为 1,则不会进入 if 条件来检查 num 的值并根据该条件中断主线程。

public class Happy_numbers {
    public static void main(String[] args) throws IOException{

        String s = "./data.txt";

        FileInputStream fin = new FileInputStream(s);
        InputStreamReader in = new InputStreamReader(fin);
        BufferedReader br = new BufferedReader(in);
        int num;
        while((s = br.readLine()) != null) {
            num = Integer.parseInt(s);

            Ishappy ishappy = new Ishappy(num,Thread.currentThread()); 
            ishappy.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                System.out.println(1);
                continue;   // here is another problem, infinit loop
            }
            if(ishappy.isAlive()) {
                ishappy.Exit();
                System.out.println(0);
            } else
                System.out.println(11);

        }
        br.close();
        in.close();
        fin.close();
        System.out.println("DONE");
    }
}

这是Ihappy课

class Ishappy extends Thread {
    private volatile Integer num;
    private Thread main;
    private volatile boolean out = false;

    Ishappy(int i, Thread main) {
        this.main = main;
        num = i;
    }

    void Exit() {
        out = true;
    }

    @Override
    public void run() {
        while(!out) {   /// <- here was the problem
            if(num.intValue() == 1) { // since this condition will break out
                main.interrupt();     // of the loop, you do not need it in the
                break;                // while condition
            }

            String s = num.toString();
            int temp = 0;
            for(int i = 0 ; i < s.length(); i++) {
                int x = Integer.parseInt(s.substring(i, i+1));
                temp += x*x;
            }
            num = temp;
        }
    }
}

输出是

1
1
0
DONE

【讨论】:

  • 嗯..是的。我想我应该记录所有测试过的数字,但我仍然不明白为什么主线程没有被中断。我不确定,但我猜 join() 是由 java 6 添加的,我应该在 java 1.5 中编写代码。
  • 那么你可以在try/catch之间使用Thread.sleep(1000)。忽略任何异常,因为必须使用我的方法中断线程。顺便说一句,在这种情况下使用 join 是没有用的。因为它将等待线程完成其职责或直到它被中断。
  • 备案 try(FileInputStream fin = new FileInputStream(s)) 是 java 1.7 风格的代码而不是 1.5
  • 哦,谢谢.. 我不知道。而且,连接并没有多大用处,因为它总是等待线程终止。我只是不明白为什么主线程没有被中断。
  • yeah.. :) 实际上这是我在将 num != 1 条件添加到 while 循环之前编写的代码,但是我在处理异常时忘记在 continue 之前添加语句 s = br.readLine(); 和它只是在无限循环中运行。不过,感谢您引起我的注意。已接受答案。
猜你喜欢
  • 1970-01-01
  • 2022-07-15
  • 1970-01-01
  • 2011-03-31
  • 2018-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多