Java在我的代码中跳跃

时间:2016-11-13 19:44:10

标签: java function thread-safety race-condition

我遇到了一个问题,即java解释我的代码完全错误,我不知道如何改变它。由于我的程序太复杂而无法解释,我将对我的问题进行概括性描述,并希望你能给我一个通用的答案。 在我的代码中有一些类似的东西:

function1();
object.function2();
function1();

function1改变了对象。

当我运行它时,我在function2内部得到一个异常(意味着代码永远不会到达第三行)。但是,当我删除第三行时,代码运行没有任何问题。这意味着java编译器正在以这样的方式编译我的代码:function1的第二次调用对调用object.function2的前一行有一些影响。 同样有趣的是,如果在第2行和第3行之间插入断点,它在调试时总是有效。

这是正常的吗? java中是否存在导致此问题的原因,以及任何阻止此问题的方法? 代码在Bitbucket Repository处可用,但要注意,它是未记录的意大利面条代码,可能违反了java代码中的所有约定。描述的问题在Pool.java里面,从第41行开始。

我希望我在这里提供的一些小信息足以作出某种解释。

1 个答案:

答案 0 :(得分:0)

好吧,我认为已经弄清楚了这一点,能够创造一个可能对你有帮助的答案(当然),并希望其他人能够帮助你。

确实涉及多线程,因为

object.function2();

正在创建 JFrame f 。该框架 f 内的所有绘图操作都由 AWT-EventQueue-0 (或任何其他编号)任务 T 执行。此任务 T 与主线程 M 并行运行。表示在 f 内的自定义 JPanel p 中绘制并由 M l (如列表) >可能会导致问题。

示例

public static void main(String[] args) {
    List<Integer> l = new ArrayList<Integer>(5);
    JFrame gui = new JFrame();
    gui.add(new MyPanel());
    l.add(10);
}

public class GUI extends JPanel {
    private List<Integer> l;
    public MyPanel(List<Integer> l) {
        this.l = l;
    }
    @Override
    public void paintComponent(Graphics g) { // this is called by JFrame.paint()
        super.paintComponent(g);
        List<Integer> modifiedL = new ArrayList<Integer>(l.size());
        for(Integer i : l) {
            modifiedL.add(2 * i);
        }
        // this is a stupid example, because it makes no sense to 
        // use this loop with l.size() here, but it shows the main problem. 
        for(int c = 0; c < l.size(); c++) { 
             somehowDrawSthWith(modifiedL.get(c));
        }
    }
}

现在,在调用创建 JFrame 之后更改 l 的大小,在尝试访问时会导致 ArrayOutOfBoundsException modifiedL.get(5),因为modifiedList可能在 l.add(10)之前创建。

(可能)解决方案 请注意,我写的可能,因为这是hacky,如果可能的话,应该避免。 我们需要主线程 M 等待所有内容都被绘制。这是一种方法: Java wait for JFrame to finish 在我们的示例中,我们可以将main更改为:

public static void main(String[] args) {
    List<Integer> l = new ArrayList<Integer>(5);
    CountDownLatch jFrameDrawing = new CountDownLatch(1);
    JFrame gui = new JFrame();
    gui.add(new MyPanel(), jFrameDrawing); // add the counter
    try {
        jFrameDrawing.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    l.add(10);
}

在绘图方法结束时倒数,如下

@Override
public void paintComponent(Graphics g) { // this is called by JFrame.paint()
    super.paintComponent(g);
    // ...
    jFrameDrawing.countDown();
}

现在请注意,在真正完全绘制 p 之前,可能会多次调用此方法。原因如下 Is the paint function in java graphics called multiple times when I add a JComponent?

所以如果你的主要看起来像这样:

public static void main(String[] args) {
    List<Integer> l = new ArrayList<Integer>(5);
    CountDownLatch jFrameDrawing = new CountDownLatch(3); // NOTE 3
    JFrame gui = new JFrame();
    gui.add(new MyPanel(), jFrameDrawing); // draw once
    gui.setSize(100,50); // draw again
    gui.setVisible(true); // draw a third time
    try {
        jFrameDrawing.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    l.add(10);
}

您需要将初始计数器设置为3。

TL; DR: 在

中使用 AWT 类(例如 JFrame
object.function2();

将创建多线程。这可能会创建竞争条件,例如同时修改列表。 请参阅Java wait for JFrame to finish,了解如何使用主代码思考绘图。