Leonardo Pina
Leonardo Pina

Reputation: 468

Why is waiting for a thread delaying updates to swing components?

I'm having trouble trying to understand why manipulating a swing component, namely a JProgressBar, just before waiting for a parallel task to finish.

In the following example, the progressBar will only activate its indeterminate mode after the thread finishes waiting for the result of a Callable, even though (I expect) that the call for setIndeterminate(true) happens before the waiting. Can someone explain why this happens?

private void thisDoesntWork(JProgressBar p){
    ExecutorService executor = Executors.newSingleThreadExecutor();
    Future<String> jobResult = executor.submit(() -> aLengthyJob());

    // This happens only after the button finishes waiting. (future.get())
    // I want to know why. Shouldn't this line happen before the thread blocks?
    p.setIndeterminate(true);

    try {
        System.out.println(jobResult.get());
    } catch (InterruptedException | ExecutionException ex) {}
}

public void createAndShowGUI(){
    JFrame frame = new JFrame("This progress bar wont work");
    JPanel panel = new JPanel();
    JButton button = new JButton("Start");
    JProgressBar progressBar = new JProgressBar();
    frame.setSize(500, 300);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

    // Problem happens withing this button's action
    button.addActionListener((e)->{          
        thisDoesntWork(progressBar);
    });

    panel.add(button);
    panel.add(progressBar);
    frame.add(panel);
    frame.setVisible(true);

}

private String aLengthyJob(){
    try {
        Thread.sleep(10000);
    } catch (InterruptedException ex) {}
    return "Done";
}

public static void main(String[] args) {
    new Test().createAndShowGUI();
}

If the waiting happens in another thread, it works as expected.

// Waiting in a third thread works fine
private void thisWorks(JButton b, JProgressBar p) {
    p.setIndeterminate(true);
    b.setEnabled(false);

    ExecutorService executor = Executors.newFixedThreadPool(2);
    Future<String> jobResult;

    jobResult = executor.submit(() -> aLengthyJob());
    executor.execute(() -> {
        try {
            System.out.println(jobResult.get());
            p.setIndeterminate(false);
            b.setEnabled(true);
        } catch (InterruptedException | ExecutionException ex) {}
    });
}

Imports:

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

Upvotes: 0

Views: 81

Answers (1)

Timothy Truckle
Timothy Truckle

Reputation: 15622

jobResult.get() waits for the end of the other thread inside the EDT, blocking it.

Maybe you want to at least link to some background on edt.– GhostCat

here are some resouces to read about the EDT:

https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

What is the event dispatching thread?

Upvotes: 4

Related Questions