在java中使用线程来解决建模面试之谜

时间:2013-12-20 11:02:36

标签: java algorithm thread-safety

我最近正在接受Java Developer的职位面试。我接到了一个任务:想一个用Java表示电路(如下图所示)的好方法。 a illustration http://oi40.tinypic.com/nnr4wj.jpgg

电路是逻辑门XOR,AND,OR等的组合。每个门有两个输入端口和一个输出端口。每个输出连接到另一个门的输入,该门一直连接到更高的门(如图所示)。使系统简单,不允许循环(尽管现实生活中的电路可以使用它们)。我被要求考虑使用以下指南在Java中表示此模型的好方法:

我得到一个电路和一个值列表,应该提供给它的输入。 我需要创建一个模型来表示Java中的电路,即我需要定义类和可用于表示电路的API。 根据输入值和门的连接方式,我需要计算所代表的电路将产生的输出。 我需要考虑一种表示电路板的方法,使用抽象类或接口,并展示对模型的理解(如果需要使用模式设计)。 我选择将系统设计成一棵树,面试官告诉我这是一个不错的选择。然后我构建这些类: 门:

public class gate_node {
    gate_node right_c,left_c;
    Oprtator op;
    int value;
    int right_v,left_v;
    public gate_node(gate_node right,gate_node left,Oprtator op){
        this.left_c=left;
        this.right_c=right;
        this.op=op;
        right_v=left_v=0;
    }
    int compute() {
        /* The following use of a static sInputCounter assumes that the static/global 
         * input array is ordered from left to right, irrespective of "depth".  */

        final int left = (null != this.left_c ?  this.left_c.compute()  :  main_class.arr[main_class.counter++]);
        final int right = (null != this.right_c ?  this.right_c.compute()  :  main_class.arr[main_class.counter++]);
        return op.calc(left, right);
    }   
}

抽象运算符

public abstract class Oprtator {
        abstract int calc(int x, int y);
}

或门:

public class or extends Oprtator {
        public int calc(int x, int y){
            return (x|y);
        }
}

和门:

public class and extends Oprtator {
        public int calc(int x, int y){
            return (x&y);
        }
}

大门上的一棵树:

public class tree {
    gate_node head;

    tree(gate_node head) {
        this.head = head;
    }

    void go_right() {
        head = head.right_c;
    }

    void go_left() {
        head = head.left_c;
    }


}

和一个主要类:

public class main_class {
    public static int arr[] = { 1, 1, 1, 0 };
    public static int counter = 0;



    public static void main(String[] args) {
        tree t = new tree(new gate_node(null, null, new and()));
        t.head.left_c = new gate_node(null, null, new and());
        t.head.right_c = new gate_node(null, null, new or());



        System.out.println(t.head.compute());
    }
}

在上面的代码中,我将电路板实现为具有当前磁头的树(可以向下/向右/向右移动)。每个节点有2个子节点(也是节点类型),2个条目(0/1),一个值和一个运算符(抽象类,可以通过OR / AND扩展..)。

我使用了一个计数器和一个数组将值插入到树的相应叶子中(如代码中所述)。

然后我的intreviwer希望我使用Threads,因为反过来将代码转换为并行计算,例如每个线程将重现一个门(我必须决定它)并将执行一个indempendet calcult,他将传递直到..直到设定最终值。 我有点不知道该做什么...... 有人有什么建议吗? 非常感谢!

3 个答案:

答案 0 :(得分:0)

由于我不是java程序员,所以我只能给你一个大概的想法。

首先区分门和树节点。

<强>水面浮油

1)将每个门实现为类/对象。它必须有2个属性:输入A,输入B和计算结果的方法;

2)实现一棵树。每个节点都是一对(gatenext_node)。 Root是next_node为空的节点。叶子是节点,没有其他节点指向它。

3)使用节点的共享(线程安全)队列。它最初是空的。

4)有一个固定的数字(选择一个,不依赖于门数),它们不断地等待队列中的一个元素(除非达到结果,在这种情况下它们才会退出)。

<强>循环

1)每当节点上发生输入时,将节点放入队列(在开始输入处转到离开)。这可以通过在门上定义add_input方法来实现。

2)线程从队列中获取节点:

3.1)如果其中一个输入丢失则丢弃它(当第二个输入出现时它会再出现一次)。另一个想法是仅在两个输入都存在时才将节点放入队列。

3.2)如果两个输入都在那里,那么计算结果并将其传递给next_node如果它不为空(并将next_node放入队列中)。如果next_node为空,那么这就是你的结果 - 打破循环并最终确定。

我说的是“循环”,虽然那里没有真正的循环。等待节点的线程就是循环。

所以基本上这个想法与你的想法相反。在您的想法中,您从root开始并递归计算输入。在我的想法中,你从叶子开始,计算值并将其推送到更高的节点。由于没有进行递归,您可以轻松地在线程之间划分作业。

我不确定这是否是你面试官想要的,但这是使用我能想到的线程的唯一解决方案。

答案 1 :(得分:0)

实现电路计算的类应该实现接口Runnable:

http://tutorials.jenkov.com/java-concurrency/creating-and-starting-threads.html

为了加快计算速度,您可以尝试将电路分成两个或多个子电路,从输入到最后一个门(给出输出的门),这样输出将是应用于最后一个门的最后一个门。这些子电路的结果。如果这些子电路不会重叠很多,通过为这些子电路启动多个线程,如果您的计算机上有多个处理器,您的确应该获得加速(如果它们重叠,您将多次执行相同的计算)。

然而,为每个门启动一个线程是没有意义的,因为启动线程比计算布尔门的输出要花费更多。

当然电路必须非常大以注意效果(否则创建线程的开销实际上会使计算速度变慢)。

答案 2 :(得分:0)

这种问题有很多种方法,所以这只是一个建议:

  • 让树中的每个对象知道其输出到哪些对象
  • 拥有一个中心对象,其中包含需要更新的对象队列。使用java.util.concurrent中的一个异步队列。
  • 拥有一个线程池(理想情况下每个CPU核心一个),从队列中提取项目,重新评估对象输出的状态,并将依赖于该输出的所有对象添加到队列中。

对于大量线程,此方案应该比单线程方法更快。您还可以通过将子系统组合为始终以单线程方式评估的对象来提高效率。