代码之家  ›  专栏  ›  技术社区  ›  Bad Request

您如何思考和预测这样的线程问题的输出?

  •  9
  • Bad Request  · 技术社区  · 14 年前

    我正在为SCJP做准备,多线程一直是我最不稳定的领域,主要是因为我不知道如何查看多线程代码并遍历它。到目前为止,我的方法是用英语写下在每个线程中可能发生的事情,并测试一些线程之间随机交叉的情况,这是一种真正的命中率和错过率,而且非常耗时的方法。所以我想看看专业人士会怎么做。你是否愿意阅读下面的代码(这是给我带来麻烦的最新问题),并在你计算出可能的输出时,写下你头脑中的想法(仅与代码相关的东西,请:)?问题的选择就在最后。我所寻找的不是我所拥有的解决方案,而是一个人如何在考试中有效地获得解决方案。

    是的,我知道这个问题没有一个准确的答案,等等。被接受的投票结果是最清晰和最容易模仿的答案,好吧:)

    谢谢大家!

    问题:这些答案中哪些是可能的输出?

    public class Threads1 {
    
        int x = 0;
    
        class Runner implements Runnable {
    
            public void run() {
                int current = 0;
                for (int i = 0; i < 4; i++) {
                    current = x;
                    System.out.print(current + ", ");
                    x = current + 2;
                }
            }
        }
    
        public static void main(String[] args) {
            new Threads1().go();
        }
    
        public void go() {
            Runnable r1 = new Runner();
            new Thread(r1).start();
            new Thread(r1).start();
        }
    }
    

    选择(选择所有适用的选项):

    A.0,2,4,4,6,8,10,6,

    B.0,2,4,6,8,10,2,4,

    C.0,2,4,6,8,10,12,14,

    D.0,0,2,2,4,4,6,6,8,8,10,10,12,12,14,14,

    E.0,2,4,6,8,10,12,14,0,2,4,6,8,10,12,14,

    3 回复  |  直到 12 年前
        1
  •  11
  •   Bert F    14 年前

    A和C (假设问题是 哪些答案是可能的输出? )

    当然,最困难的部分不是你找到可能的解决方案。但更确切地说,是要仔细观察你认为是 可能的,试着说服自己你有一个不可能的理由 你已经排除了所有绕过你的理由的方法。

    到目前为止,我的方法是用英语写下每一个线程中可能发生的事情…

    你需要弄清楚每一个号码是哪根线印的。下面是我能想到的最有效、最简洁的格式,它可以让你在处理各种可能性的时候很容易地划掉/擦除/重写。认识到:

    • 一旦你找到一个可能的答案 继续前进 . 不管它在现实世界中是不可能的,还是可能有其他可能(或不可能)的组合。只要你找到一个可能性,这就是你所需要的 继续前进 .

    • 首先尝试最简单的方法,例如假设每个数字都是T1,直到碰到一个不可能是T1的数字,然后填写t2,依此类推。希望你能在没有矛盾(或容易解决的矛盾)的情况下达到目的。找到可能的组合后,继续。

    • 随意跳到 消除 可能的很快,所以你可以把注意力集中在可能不可能的事情上。

    以下是我的草稿纸/工作表的最终编辑(附在我的心理注释之后):

    A. 0, 2, 4, 4, 6, 8, 10, 6,
       1  1  1  2  2  2   2  1     <- possible threads that produced this output - possible solution
    
    B. 0, 2, 4, 6, 8, 10, 2, 4,
       1  2  2  2  2   ?  1        <- to print second '2', T1 interrupted between L10/L11; 4 passes of T2 used up
    
    C. 0, 2, 4, 6, 8, 10, 12, 14,
       1  1  1  1  2   2   2   2   <- possible solution - simplest solution (T2 waits until T1 is completely done) - doesn't matter that it isn't likely, just that is possible
    
    D. 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14,
       1  2  1  2  1  2  1  2  1  2   ?    <- threads used up
    
    E. 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
       1  1  1  1  2   2   2   2  ?   <- threads used up
    

    注:

    http://download.oracle.com/javase/tutorial/essential/concurrency/atomic.html

    • 对于引用变量和大多数基本变量(除long和double之外的所有类型),读和写都是原子的。

    原子动作不能交错,因此可以在不担心线程干扰的情况下使用。

        2
  •  5
  •   linuxuser27    14 年前

    我处理多线程问题的方法是分解线程将运行的代码,然后确定有多少线程将运行该代码,以及是否访问其他线程可能使用的任何变量。

    在这个例子中,有3个线程。这个 main 线程调用 new Threads1().go(); 创造 r1 ,然后再启动两个线程, new Thread(r1).start(); 新线程(r1).start(); . 现在我们知道有多少线程可以跟踪它们将要执行的操作。

    这个 主要的 线程从返回后将死亡 go() .

    另外两个线程将分别进入 run() 方法 Runner 对象, R1 . 现在我们还知道每个线程都将执行 () . 那么让我们看看 () 做。它有一个 for 每次执行循环时打印输出的循环。因此呼吁 () 将打印 4 时代。因此每个线程将打印2个线程 时代。因此,输出不能超过 8 数字。

    关于这些数字是什么是不可能的,因为 转轮 实例对于每个线程都是相同的 x 变量可以根据另一个调用的线程进行更改。 () . 因此,您需要确定的是,数字序列是否可能?对于A和C来说,这个问题的答案是“是”。这是因为您不知道每个线程何时会被另一个线程抢占,并且由于在循环期间有一个本地副本正在生成,所以您可以获得一些非常独特的顺序。

    如下文所述,选项B即使有8个输出,也是不可能的。尝试想出一个线程序列来创建该输出。

        3
  •  0
  •   Gray droiddeveloper    14 年前

    这个问题比看起来更具欺骗性——我越想越喜欢它。我开始讨论我如何看待线程程序——我分析同步点和I/O调用。但除了没有内部同步的println之外,没有其他的。所以我们只剩下随机时间(见 race conditions )这结合了没有保证的方法来同步 x 线程之间意味着它是随机的。

    但是,如果我们看看答案,我们可以看到一些答案是不可能出现的。例如,正如@linuxuser27所指出的,每个线程只打印4个数字,这将删除答案,将打印更多的数字。另一个不正确答案的例子是,如果任何答案的值大于14,因为第一个线程可以变为0,2,4,6,第二个线程可以变为8,10,12,14,但不能更高。

    因为每个线程必须打印4次数字,并且数字由 每个线程 必须至少是2+上一个线程打印的数字,某些模式无法生成。我不会给出我的答案,但不是“以上全部”,也不是A、B和C。