0

I'm trying to learn Threads in Swing.

I have a Frame with a JProgressBar (progress), five JButtons (Start, Suspend, Resume, Cancel, Close), and a JLabel (label1).

The frame opens. Only Start is enabled. Start calls my class Progressor:

Updated Again Once and For All

Progressor progressor; //declared as class variable, initialized new in constructor and again in overridden done method

Here's the ButtonListener class:

public class ButtonListener implements ActionListener{
    public void actionPerformed(ActionEvent e)
    {

        if (e.getSource() == jbStart) {
            progressor.execute();
            label1.setText("Progressing ...");
            jbCancel.setEnabled(true);
            jbResume.setEnabled(true);
            jbSuspend.setEnabled(true);
            jbClose.setEnabled(true);
        }
        if(e.getSource() == jbCancel) {
            progressor.cancel(true);
            label1.setText("Progress Canceled");
        }
        if (e.getSource() == jbSuspend) {
            label1.setText(progressor.suspendProgress());
        }
        if (e.getSource() == jbResume) {
            label1.setText(progressor.resumeProgress());
        }
        if (e.getSource() == jbClose) {
            dispose();
        }

    }

}//buttonlistener

Here's the SwingWorker class:

public class Progressor extends SwingWorker<Void, Integer> {

    private volatile boolean suspend = false;
    private Object lock = new Object();

    @Override
    protected Void doInBackground() {

        for (int i = 0; i <= 10; i++) {
            checkForSuspend();
            try {
                Thread.sleep(1000);
            }
            catch (InterruptedException e) {
            }
            publish(i);
        }   
        return null;
    }

    @Override
    protected void process(List<Integer> list) {
        int value = list.get(list.size() - 1);
        progress.setValue(value);

    }

    public void checkForSuspend() {
        synchronized (lock) {
            while (suspend) {
                try {
                    lock.wait();
                } catch (InterruptedException ie){
                }
            }
        }
    }//checkForSuspend

    @Override
    protected void done() {
        label1.setText("All Done.  Press close to exit");
        progressor = new Progressor();
    }

    public synchronized String suspendProgress() {
        suspend = true;
        return "Progress suspended ...";
    }

    public synchronized String resumeProgress() {
        synchronized (lock) {
            suspend = false;
            lock.notify();
            return "Progress resumed ...";
        }
    }


}//Progressor class

Everything works except the cancel doesn't doesn't actually cancel the thread (the progress bar continues).

Should I suspend it before canceling?

MayNotBe
  • 2,110
  • 3
  • 32
  • 47

2 Answers2

0

This How to Pause and Resume a Thread in Java from another Thread question looks very similar to yours and has some nice examples in the answers.

As for your own code and why it does not work:

  1. You create a new progressor on every click. You should be using and controlling one, instead of creating new ones every time.
  2. When suspending your progressor finishes work instead of suspending. As the above question states - you should be looking at the flag at some points of your computation and acting on it. Example:

    while (!cancel) {
        if (!suspended) {
            for (int i = 1; i <= 10; i++) {
                    Thread.sleep(1000);
                    publish(i);
            }
        }
    }
    

    The above code will suspend when it next reaches 10 (unless you resumed it before that), and finish when you press cancel (Cancel needs to be added as an extra flag in the obvious manner).

Community
  • 1
  • 1
Ordous
  • 3,844
  • 15
  • 25
  • Thank you for the link but I thought SwingWorker could only be used once. Where and how do I create one to control with the buttons? – MayNotBe Apr 29 '14 at 18:08
  • @MayNotBe Indeed, SwingWorker, just like any Thread, cannot have `execute()` invoked more than once. The idea is to create and execute it once (Probably in your form initialization, or first time you press start), and then in that execution to look at the controlling flags – Ordous Apr 29 '14 at 18:12
  • I don't know. I did that - created the progressor outside of the buttonlistener and used the code you suggested and it's still not working. The Cancel works with p.cancel(true); but it's like the thread isn't checking the flags as it progresses. – MayNotBe Apr 29 '14 at 19:05
  • @MayNotBe Could you post the updated code? Just reminding, that the thread will suspend ONLY on count 10, i.e. if you suspend at 3, it will still count up to 10 before suspending – Ordous Apr 29 '14 at 19:39
  • I just posted the updated code in the original question. You're right, of course it's not interrupting the `for`. But how can I interrupt the `for` loop to check `suspend`? Of course, why do I need the `for`? I can just increment in a `while` loop and it would check everytime! – MayNotBe Apr 29 '14 at 20:07
  • Ok, I just saw the `checkForPaused()` method in the linked answer. Let's see if I can work this out. – MayNotBe Apr 29 '14 at 20:36
  • Hey, thanks for all your help with this! I updated the post with the finished code. I have a way better handle on everything - only thing not working is the cancel, should I suspend prior to canceling? – MayNotBe Apr 30 '14 at 00:11
  • What you could do is: After check for suspend check cancel flag, if true - break. On cancel wake the suspend object. – Ordous Apr 30 '14 at 09:16
  • I understand your first sentence with a cancel flag and break, but not the second: What do you mean wake the suspend object on cancel? – MayNotBe Apr 30 '14 at 13:07
  • I get how but I don't understand why I'm waking it. Which, I think, speaks to a fundamental misunderstanding of concurrency :) – MayNotBe Apr 30 '14 at 16:55
  • @MayNotBe Imagine you have pressed suspend, and then cancel. If you don't wake on cancel, then the thread will not do anything (it's suspended), but will hang indefinetely, since it never gets to cancelling either. Of course if you have not pressed suspend before cancel then there is no need to notify. – Ordous Apr 30 '14 at 16:58
  • Ok, I get that. I was thinking that I should notify even if not suspended. Hey, thanks again for everything. Very helpful! – MayNotBe Apr 30 '14 at 17:14
0

Your thread should run inside a while loop that looks for a boolean to change value from another object, then simply change the state with setPause(true/false) when you click the button:

while(true){
    if(object_everyone_can_reference.getPause()){
    Thread.sleep(1000);
     }

 }
Petro
  • 3,484
  • 3
  • 32
  • 59