Java JProgressBar没有显示setVisible(true)

时间:2014-08-01 03:56:43

标签: java multithreading swing thread-safety

我有一个像下面这样的方法。

ProgressWindowJFrame的子类,包含JProgressBar

addProgress()会增加JProgressBar

中的值

如果我从另一个类的方法调用此方法,则ProgressWindow的框架将显示在框架内但不会显示JProgressBar和一些JLabels。它们显示在最后一行(System.out.println("finish"))之后。

如果我在包含此方法的类中的main方法中调用此方法,则会立即显示每个组件(Bar,labels ...)。

如何才能正确显示窗口?

static void search(){
  ProgressWindow window = new ProgressWindow();
  window.setVisible(true);
  ExecutorService execs = Executors.newFixedThreadPool(Runtime
            .getRuntime().availableProcessors());
  Collection<Callable<Void>> processes = new LinkedList<>();
  for (int i = 0; i < 100; i++) {
    processes.add(new Callable<Void>() {
        @Override
        public Void call() throws Exception {
            progressWindow.addProgress(); // increment progress value
            return null;
        }
        });
    }
    try {
        execs.invokeAll(processes);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        execs.shutdown();
    }
    System.out.println("finish");

2 个答案:

答案 0 :(得分:1)

听起来你想要做的是调用Swing UI线程上的setVisible,你可以使用invokeAndWaitinvokeLater执行此操作。

类似于:

final ProgressWindow window = new ProgressWindow();
SwingUtilities.invokeLater(new Runnable() {            
    @Override
    public void run() {
        window.setVisible(true);                
    }
});

答案 1 :(得分:1)

主要问题是您似乎是从事件调度线程的上下文中调用search

出现问题的原因是您使用execs.invokeAll阻止所有callables运行完毕。

这意味着EDT无法处理事件队列中的新事件,包括重新绘制事件,这就是您的用户界面静止不动的原因......

您现在面临许多问题......

  1. 您永远不应该从EDT以外的任何线程更新/修改UI组件
  2. 您应该出于任何原因阻止EDT
  3. 您似乎想知道搜索何时完成,因此您需要某种事件通知......
  4. 我们需要的第一件事是通知搜索已完成的通知,这意味着您在搜索完成时无法再依赖search返回...

    public interface SearchListener {
        public void searchCompleted();
    }
    

    接下来,我们需要一种中间search方法来构建用户界面,并确保在其自己的Thread内启动搜索...

    static void search(final SearchListener listener) {
      final ProgressWindow window = new ProgressWindow();
      window.setVisible(true);
    
      Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            search(listener, window);
        }
      });
      t.start();
    }
    

    然后我们需要修改原始search方法,以便在搜索完成时使用SearchListener界面提供通知......

    static void search(final SearchListener listener, final ProgressWindow window){
      ExecutorService execs = Executors.newFixedThreadPool(Runtime
                .getRuntime().availableProcessors());
      Collection<Callable<Void>> processes = new LinkedList<>();
      for (int i = 0; i < 100; i++) {
        processes.add(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                // This method needs to ensure that 
                // what ever it does to the UI, it is done from within
                // the context of the EDT!!
                progressWindow.addProgress();
                return null;
            }
            });
        }
        try {
            execs.invokeAll(processes);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            execs.shutdown();
        }
        System.out.println("finish");
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                listener.searchCompleted();
            }
        });        
    }
    

    现在,如果没有addProgress的源代码,我可能会想要使用

    processes.add(new Callable<Void>() {
        @Override
        public Void call() throws Exception {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    progressWindow.addProgress();
                }            
            });
            return null;
        }
        });
    }
    

    ,而不是...

    请查看Concurrency in Swing了解详情