How to share information between GUI threads?

253 views Asked by At

I’m writing a client application with an accompanying GUI (Swing). My two classes, ClientClass and MainFrame, are running different threads yet need to call methods in one another. ClientClass instantiates the GUI (MainFrame) at some point during the applications life cycle on the EventQueue thread (displayGUI()). ClientClass contains many methods like recv(), called from the client classes thread, that updates MainFrame. In turn, MainFrame has methods that are triggered by events like the push of a button which call methods in ClientClass. I’m assuming the annoymous method that handles a button press in the example is being called by the EventQueue thread?

I’m pretty sure this kind of application is very common and I’d love the insight of others. I have a feeling that what I’m doing isn’t thread safe, so how can I fix/improve the current model of this application?

Example code:

MainFrame.java:

public MainFrame(ClientClass c) {
    client = c;

    // <Misc init code here>

    btnSend = new JButton("Send");
    btnSend.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent arg0) {
            client.send("Hello!");
        }
    });
    btnSend.setBounds(171, 120, 89, 23);
    contentPane.add(btnSend);
}

public void updateElement() {
    // Update of some element here, called from ClientClass
}

ClientClass.java:

private MainFrame mainFrame;

public ClientClass() {
}

public void displayGUI() {
    final ClientClass c = this;

    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                mainFrame = new MainFrame(c);
                mainFrame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

public void send(String msg) {
    // Socket send operations here
    // Currently called by the GUI's EventQueue thread?
}

public void recv() {
    // Socket recv operations here
    mainFrame.updateElement();
}
2

There are 2 answers

3
Xeon On BEST ANSWER

You can use SwingUtilities to invoke code on EDT (on UI thread).

Mouse clicked is invoked on UI thread - so you should invoke send in the background (to not block UI if it takes long).

On your recv method you should invoke mainFrame.updateElement(); on UI thread if you change GUI state (JLabel text, etc.) - you do this by:

SwingUtilities.invokeLater(new Runnable() {... //

Generally speaking - anything you do that may impact GUI elements (changing text, invalidating, adding components, etc.) you should do on EDT.

And everything that may block user interface you should do in background - spawning new Thread.

If you need to block any events and wait to background - you can show modal JDialog (remember that you should hide it in finally block - just in case)

You should also look at the SwingWorker class and at the tutorial.

0
Panky On

Xeon's answer is probably more directly useful to you in this particular case, but as a general principal you may want to read up on Singletons.

By creating a single Singleton (which would often be called a Manager or some-such in the case you are describing) you can have a single class which performs the 'work' associated with your application, and have the GUI thread(s) send tasks to that singleton.