0

I want to use thread to change the content of JLabel when clicking one button. this button will process one document which cost lots of time. But when I click the button, the content of the JLabel will be showed after processing the document. I want it can be showed immediately.

here is the code

public void actionPerformed(ActionEvent e) {
        // TODO Auto-generated method stub
        String command = e.getActionCommand();
        // Load button
        if (command.equals("Load")) {
            // set load to true
            load = true;
            // get text area name
            name = nameField.getText();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    loadState.setText("Loading document "+name+" . Please wait!");
                }           
            }).start();
            // load daffodils
            if (name.toLowerCase().equals("daffodils.txt")) {
                try {
                    loadText(tpPoem);
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
}

anyone can tell me why this thread doesnt work.

Peter Bagyinszki
  • 1,069
  • 1
  • 10
  • 29
Daxi Song
  • 67
  • 1
  • 4
  • It's hard to know what's going on here. Maybe at least post how the variables are declared? – Kcits970 Jan 11 '20 at 11:05
  • Assuming `loadState` is the JLabel, try declaring `loadState` as `volatile`. – Kcits970 Jan 11 '20 at 11:09
  • Does this answer your question? [Updating GUI from a runnable](https://stackoverflow.com/questions/10365404/updating-gui-from-a-runnable) – Progman Jan 11 '20 at 14:16

2 Answers2

1

Swing is mostly single-threaded, invoking methods from multiple threads may cause problems. You should modify your label on a special thread called the event dispatch thread (EDT).

ActionListener.actionPerformed runs on this thread, so instead of

new Thread(new Runnable() {
    @Override
    public void run() {
        // TODO Auto-generated method stub
        loadState.setText("Loading document "+name+" . Please wait!");
    }           
}).start();

You can write:

loadState.setText("Loading document "+name+" . Please wait!");

Your loading also seem to be running on the EDT:

if (name.toLowerCase().equals("daffodils.txt")) {
    try {
        loadText(tpPoem);
    } catch (IOException e1) {
        e1.printStackTrace();
    }
}

If you use a background thread in that method then it's OK, otherwise check out SwingWorker. That is for to perform lengthy tasks in a background thread. If you block the EDT with a long running task, the label will not be updated until the task is finished.

If you want to update your GUI from the background thread SwingWorker has methods for that. Besides, you might find this question interesting: When and Where to call EventQueue.invokeLater() method

Peter Bagyinszki
  • 1,069
  • 1
  • 10
  • 29
0

Try this:

public interface IWorkerThreadCallback {

    void onSuccess(boolean result);

    void onError(@NonNull Throwable result);
}


public class WorkerThread implements Runnable {
    private IWorkerThreadCallback callback;

    public WorkerThread(@NonNull IWorkerThreadCallback callback){
        this.callback = callback;
    }

    @Override
    public void run() {
        try {

            // do long task on a background thread

            // return when task is success
            callback.onSuccess(true);
        }
        catch (Exception ex){
            // return when task is error
            callback.onError(ex.fillInStackTrace());
        }
    }
}

// call worker thread from main thread

 new Thread(new WorkerThread(new IWorkerThreadCallback() {
     @Override
     public void onSuccess(boolean result) {
         // success result
     }

     @Override
     public void onError(@NonNull Throwable result) {
         // error result
     }
 })).start();
koding
  • 155
  • 1
  • 9