My GUI is frozen

后端 未结 6 1723
青春惊慌失措
青春惊慌失措 2020-12-11 13:45

I have something I can\'t understand: my Swing GUI contains a \'play\' and \'pause\' button. I have also a static variable that defines \'ON\' and \'OFF\' states. (The main

相关标签:
6条回答
  • 2020-12-11 13:55

    You should not start long-running processes in Swing’s event handler because it will freeze your GUI, you know that now. :) Start it in a new thread. You only need to use a SwingWorker if you’re planning on manipulating the GUI from the worker thread (because Swing is not thread-safe).

    0 讨论(0)
  • 2020-12-11 13:55

    This is a pretty straightforward reason: while Java is working on your time-consuming process, it isn't able to update the GUI. Solution: run the time-consuming process in a separate thread. There are a bunch of ways to program that, and it would probably depend somewhat on how your program is written.

    0 讨论(0)
  • 2020-12-11 14:03

    The event dispatch thread (EDT) is the only thread in which it's safe to read or update the GUI.

    The pause button should be setting the on/off variable in the event dispatch thread.

    The time-consuming operation, and the loop, should not be in the EDT. (The loop should also not be running continuously doing nothing but check the variable, or it can easily eat all your CPU. If it has nothing else to do it should check, and then call Thread.sleep() for some length of time (say 100ms).)

    If you can prove that the on/off variable is being set to OFF, but that nonetheless it's always read as ON, it may be that the variable's value is not being copied from the EDT to the worker thread. Make it volatile, or synchronize access to it, or use an AtomicReference, or read it in the EDT using SwingUtilities.invokeAndWait().

    SwingWorker probably is the simplest way to go, here. Implement your time-consuming operation, and the on/off check, in the doInBackground() method, and your GUI update in the done() method.

    public enum State {
        RUNNING, STOPPED
    }
    
    public class ThreadSafeStateModel {
        private State state = State.STOPPED;
    
        public synchronized void stop() {
            state = State.STOPPED;
        }
    
        public synchronized void start() {
            state = State.RUNNING;
        }
    
        public boolean isRunning() {
            return state == State.RUNNING;
        }
    }
    
    public class ExpensiveProcessWorker extends SwingWorker<Void, Void> {
    
        private final ThreadSafeStateModel model;
    
        public ExpensiveProcessWorker(ThreadSafeStateModel model) {
            this.model = model;
        }
    
        @Override // Runs in background
        protected Void doInBackground() throws Exception { 
            while (model.isRunning()) {
                // do one iteration of something expensive
            }
            return null;
        }
    
        @Override // Runs in event dispatch thread
        protected void done() { 
            // Update the GUI
        }
    }
    
    public class StopButton extends JButton {
        public StopButton(final ThreadSafeStateModel model) {
            super(new AbstractAction("Stop") {
                @Override
                public void actionPerformed(ActionEvent e) {
                    model.stop();
                }
            });
        }
    }
    
    public class StartButton extends JButton {
        public StartButton(final ThreadSafeStateModel model) {
            super(new AbstractAction("Start") {
                @Override
                public void actionPerformed(ActionEvent e) {
                    model.start();
                    new ExpensiveProcessWorker(model).execute();
                }
            });
        }
    }
    

    (A lot could be done to clean this up depending on the real application, but you get the idea.)

    0 讨论(0)
  • 2020-12-11 14:10
    @Override
    public void actionPerformed(ActionEvent e) {
         new Thread() {
            public void run() {
                //your code which runs on click event
            }
        }.start();
    
    }
    
    0 讨论(0)
  • 2020-12-11 14:11

    You need to read Concurrency in Swing to understand how the EDT and SwingWorkers operate.

    All GUI updates are executed on the EDT so when you click a GUI component any method that this calls will be executed on the EDT. If this is a time consuming process then this will block the EDT from executing any futher GUI updates. Hence your GUI is freezing and you can't click the pause button.

    You need to use SwingWorker to execute the time consuming process on another thread. The link I provided above details how to do this.

    0 讨论(0)
  • 2020-12-11 14:14

    The problem is that you're doing the intensive, time-consuming work on the same thread responsible for updating the GUI. SwingWorker allows you to move time-consuming tasks to a separate thread of execution, thereby leaving the UI thread to do its thing uninhibited.

    However, it does add a further complication: affinity. Calling methods on UI components generally requires that you do so from the UI thread. Therefore, you need to use special functionality to get back to the UI thread from the worker thread. SwingWorker also gives you this ability.

    I suggest you read through this documentation.

    0 讨论(0)
提交回复
热议问题