【问题标题】:JGraph AWT EventQueue exception on paint (with multithreading)油漆上的JGraph AWT EventQueue异常(使用多线程)
【发布时间】:2011-10-05 06:44:05
【问题描述】:

情况

我有一个使用JGraph 的可视化。该图由不同的线程更新为实例化可视化的线程。

预期行为

图表应该由各种工作线程更新。线程调用更新图的函数是syncronized,所以工作线程之间不会引起并发问题。

实际行为

绘制时(偶尔)在 AWT-EventQueue 线程中抛出异常。有时它是一个空指针,有时它是一个超出范围的索引。这是一个堆栈跟踪:

线程“AWT-EventQueue-0”中的异常 java.lang.NullPointerException 在 com.mxgraph.shape.mxConnectorShape.translatePoint(未知来源) 在 com.mxgraph.shape.mxConnectorShape.paintShape(未知来源) 在 com.mxgraph.canvas.mxGraphics2DCanvas.drawCell(未知来源) 在 com.mxgraph.view.mxGraph.drawState(未知来源) 在 com.mxgraph.swing.mxGraphComponent$mxGraphControl.drawCell(未知来源) 在 com.mxgraph.swing.mxGraphComponent$mxGraphControl.drawChildren(未知来源) 在 com.mxgraph.swing.mxGraphComponent$mxGraphControl.drawCell(未知来源) 在 com.mxgraph.swing.mxGraphComponent$mxGraphControl.drawChildren(未知来源) 在 com.mxgraph.swing.mxGraphComponent$mxGraphControl.drawCell(未知来源) 在 com.mxgraph.swing.mxGraphComponent$mxGraphControl.drawChildren(未知来源) 在 com.mxgraph.swing.mxGraphComponent$mxGraphControl.drawCell(未知来源) 在 com.mxgraph.swing.mxGraphComponent$mxGraphControl.drawFromRootCell(未知来源) 在 com.mxgraph.swing.mxGraphComponent$mxGraphControl.drawGraph(未知来源) 在 com.mxgraph.swing.mxGraphComponent$mxGraphControl.paintComponent(未知来源) 在 javax.swing.JComponent.paint(JComponent.java:1029) 在 com.mxgraph.swing.mxGraphComponent$mxGraphControl.paint(未知来源) 在 javax.swing.JComponent.paintChildren(JComponent.java:866) 在 javax.swing.JComponent.paint(JComponent.java:1038) 在 javax.swing.JViewport.paint(JViewport.java:764) 在 javax.swing.JComponent.paintToOffscreen(JComponent.java:5138) 在 javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:302) 在 javax.swing.RepaintManager.paint(RepaintManager.java:1188) 在 javax.swing.JComponent._paintImmediately(JComponent.java:5086) 在 javax.swing.JComponent.paintImmediately(JComponent.java:4896) 在 javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:783) 在 javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:735) 在 javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:677) 在 javax.swing.RepaintManager.access$700(RepaintManager.java:58) 在 javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1593) 在 java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:226) 在 java.awt.EventQueue.dispatchEventImpl(EventQueue.java:647) 在 java.awt.EventQueue.access$000(EventQueue.java:96) 在 java.awt.EventQueue$1.run(EventQueue.java:608) 在 java.awt.EventQueue$1.run(EventQueue.java:606) 在 java.security.AccessController.doPrivileged(本机方法) 在 java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:105) 在 java.awt.EventQueue.dispatchEvent(EventQueue.java:617) 在 java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:275) 在 java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:200) 在 java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190) 在 java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:185) 在 java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:177) 在 java.awt.EventDispatchThread.run(EventDispatchThread.java:138)

它似乎不会对应用程序的运行产生不利影响。产生了一个新的 AWT-EventQueue,它在某个阶段不可避免地会遇到同样的问题。

原因

我认为这一定是由于将单独线程中的图形更新为实例化 JFrame 的线程造成的。在绘制方法试图绘制它们时,需要绘制的东西正在改变。

问题

我该如何解决这个问题?我可以以某种方式将绘制方法与更新图形的方法同步吗?

可视化代码

package ui;

import javax.swing.JFrame;

import com.mxgraph.layout.mxOrganicLayout;
import com.mxgraph.layout.mxStackLayout;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.view.mxGraph;

import core.Container;
import core.Node;
import core.WarehouseGraph;

public class Visualisation extends JFrame{
    private static final long serialVersionUID = 8356615097419123193L;
    private mxGraph graph = new mxGraph();
    Object parent = graph.getDefaultParent();
    mxOrganicLayout graphlayout = new mxOrganicLayout(graph);
    mxStackLayout containerLayout = new mxStackLayout(graph, true, 10);

    public Visualisation(WarehouseGraph model){
        super("Warehouse Simulator");

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(800, 600);

        graph.getModel().beginUpdate();
        try{
            for(Node node : model.getNodes().values()){
                mxCell cell = (mxCell)graph.insertVertex(parent, node.getId(), node.getId(), 0, 0, 60, 30);

                Object prev = null;
                for(Container container : node.getContainers()){
                    Object newCont = graph.insertVertex(cell, container.getId(), container.getId(), 0, 0, 60, 20);
                    if(prev != null) graph.insertEdge(cell, null, null, prev, newCont);
                    prev = newCont;
                }
            }

            for(Node node : model.getNodes().values()){
                for(Node toNode : node.getDownstreamNodes()){
                    Object fromCell = ((mxGraphModel)graph.getModel()).getCell(node.getId());
                    Object toCell = ((mxGraphModel)graph.getModel()).getCell(toNode.getId());
                    graph.insertEdge(parent, null, null, fromCell, toCell);
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            graph.getModel().endUpdate();
        }

        graphlayout.execute(parent);

        Object[] nodes = mxGraphModel.getChildVertices(graph.getModel(), parent);
        for(Object cell : nodes){
            containerLayout.execute(cell);
            graph.updateCellSize(cell);
        }


        mxGraphComponent graphComponent = new mxGraphComponent(graph);
        getContentPane().add(graphComponent);

        this.setVisible(true);
    }

    //CALLED FROM SYNCHRONIZED FUNCTION
    public void moveContainer(Container container, Node to){
        graph.getModel().beginUpdate();
        mxCell toCell = (mxCell)((mxGraphModel)graph.getModel()).getCell(to.getId());
        mxCell containerCell = (mxCell)((mxGraphModel)graph.getModel()).getCell(container.getId());
        mxCell fromCell = (mxCell)containerCell.getParent();

        try{
            Object[] edges = mxGraphModel.getEdges(graph.getModel(), containerCell);
            graph.removeCells(edges);

            containerCell.removeFromParent();

            graph.addCell(containerCell, toCell);

            Object[] containers = mxGraphModel.getChildVertices(graph.getModel(), toCell);
            if(containers.length >= 2)
                graph.insertEdge(toCell, null, null, containerCell, containers[containers.length-2]);
            containerLayout.execute(toCell);
            containerLayout.execute(fromCell);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            graph.getModel().endUpdate();
        }
    }
}

控制器代码

package core;
import serialisation.JsonUtil;
import ui.Visualisation;

public class Controller{
    private WarehouseGraph graph;
    Visualisation viz;

    public static void main(String[] args){
        Controller.getInstance();
    }

    private Controller(){
        graph = JsonUtil.initFromJson(); //graph has many worker threads running which can call containerMoved
        viz = new Visualisation(graph);
    }

    private static class SingletonHolder{
        private static final Controller INSTANCE = new Controller();
    }

    public static Controller getInstance(){
        return SingletonHolder.INSTANCE;
    }

    public synchronized void containerMoved(Container container, Node from, Node to){
        viz.moveContainer(container, to);
    }
}

【问题讨论】:

    标签: java swing concurrency awt paint


    【解决方案1】:

    不回答您的问题,只是对您的概念的一些通知。

    1. 您能告诉我们您的public static void main(String[] args) {...} 方法吗?我希望有些东西看起来像:

      public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() {

              public void run() {
                  Visualisation vis = new Visualisation();
              }
          });
      }
      
    2. this.setSize(800, 600); 应该是this.setpreferredSize(new Dimension(800, 600));

    3. 然后在最后一行前加上this.pack(); (this.setVisible(true);)

    编辑:

    1. 什么类型的组件返回mxGraphComponent graphComponent = new mxGraphComponent(graph);,因为如果是基于JPanel的自定义组件那么

    getContentPane().add(graphComponent); 应该是add(graphComponent);

    1. 来自后台线程/任务的所有输入都必须包装到 invokeLater() 中。如果它闪烁或冻结,那么您必须寻找invokeAndWait()。我个人讨厌invokeAndWait() 存在那里,但是对于这种方法我不能给你一些正确的建议。或者:

    2. 如果您要在某些定期基础上运行您的任务,那么您必须寻找 Runnable(输出必须包装到 invokeLater)或 SwingWorker

    【讨论】:

    • 1:在 Controller 类中。它只是实例化了 Controller 单例,而后者又初始化了 Visualiser。不过SwingUtilities.invokeLater 不行。那有什么作用?
    • 2:您不应该按照this question 对顶级组件(没有布局管理器的组件)执行setSize 吗?
    • 3:哦,pack 与setPrefferedSize 一起做了一些时髦的事情?
    • 非常感谢。听起来工作线程上的 invokeLater 应该可以解决我的问题。我会试一试,然后告诉你进展如何。
    • to your add1) 不要在运行时/飞行中创建大量 JFrame,只创建一个 JFrame,其他顶级容器将是 JDialog 或 JWindow(未装饰的容器),只创建一两个 JDialog或 JWindow 并通过 removeAll(); 重用这些顶级容器,从而避免内存不足和另一个来自 AWT 或 Swing 的意外错误,
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-02
    • 2013-04-28
    • 2015-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多