有一个AVLTreePanel类,传入一个AVL树,需要在构造函数做一些预处理,然后再在paintComponent方法中使用预处理的数据通过遍历这棵树把它画出来。
问题是,在这过程中,AVL树的结构可能发生变动,这样就会导致预处理的数据和树本身不一致的现象,甚至在预处理时就会产生问题。paintComponent方法还是JAVA在别的线程中自己调度的……
有什么好的策略可以解决这个问题么?

解决方案 »

  1.   

    我觉得可以在数据变化的线程里用repaint();方法
    数据每变化一次就调用repaint() 方法
    paintComponent()方法只需要把数据画成图形,不需要放入线程里
      

  2.   

    paintComponent() 是在一个名叫 EDT 的线程中调用的。EDT本身就是为了简化绘图的同步问题,本身就是为了解决你描述的这种问题而设计的。其核心思想就是,所有影响到绘图的状态修改,以及绘图本身,都应该在同一个线程内完成,这个线程就是EDT。这就是所谓"单线程的UI设计"。具体到你的问题,把对AVL树作出更改的代码同步进EDT就行了,假设你有一个Runnable r,下面两种方式都能保证其在EDT内运行:
    Runnable r = initTask();
    SwingUtilities.invokeLater(r);Runnable r = initTask();
    if( SwingUtilities.isEventDispatchThread() ) {
      
      r.run();
    }
    else {  try {    SwingUtilities.invokeAndWait(r);
      }
      catch(Exception ex) {    // ...
      }
    }
    后者是同步调用,会使调用线程等待,慎用。
      

  3.   

    另外,Swing控件下注册的各种listener本来就是在EDT中被调用的,比如ActionListener的actionPerformed()方法等。下面的代码可以检查当前线程是不是EDT:
    if( SwingUtilities.isEventDispatchThread() ) {
      
      // ...
    }

    if( EventQueue.isDispatchThread() ) {
      
      // ...
    }
      

  4.   

    再另外,跟据上述不难理解,任何Swing控件的建立都应该在EDT中进行。下面这种常见的GUI程序的写法,严格来说是错误的:
    public static void main(String[] args) {  JFrame frame = new CustomizedFrame("title");
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setVisible(true);
    }
    应该这样写:
    public static void main(String[] args) {  SwingUtilities.invokeLater(new Runnable() {
        
        @Override
        public void run() {      JFrame frame = new CustomizedFrame("title");
          frame.pack();
          frame.setLocationRelativeTo(null);
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.setVisible(true);
        }
      });
    }