Search code examples
javamultithreadingjava.util.concurrent

How to suspend Thread using java.util.concurrent?


I have looked everywhere. How to suspend/pause it by code until I call it to awake using any java.util.concurrentmethods/objects? I have simple Thread with run method:

When I press button it stops then starts but the problem is that I get exception when I start it again. I want it play/pause like in the media player.

Exception in thread "AWT-EventQueue-0" java.lang.IllegalMonitorStateException

Full working code(with exceptions):

import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

public class Window extends JFrame {
    ThreadPanel leftPanel, rightPanel;
    Thread leftThread, rightThread;

    public Window() {
        super("StopResume");
    }

    public void createGUI() {
        setLayout(new GridLayout());
        add(leftPanel = new ThreadPanel());
        add(rightPanel = new ThreadPanel());
        leftThread = new Thread(leftPanel);
        rightThread = new Thread(rightPanel);
        leftThread.start();
        rightThread.start();
        setSize(800, 600);
        setVisible(true);
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                int confirmed = JOptionPane.showConfirmDialog(null, "Zamknąć", "Potwierdzenie", JOptionPane.OK_CANCEL_OPTION);
                if (confirmed == JOptionPane.OK_OPTION) {
                    dispose();//tu podmienic kod
                    System.exit(1);
                }
            }
        });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Window().createGUI();
            }
        });

    }

}

 import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class ThreadPanel extends JPanel implements Runnable {
    public static final String SUSPENDED = "GO", RUNNING = "SUSPEND";
    JTextArea txt;
    JButton ppButton;
    DateFormat dateFormat;
    Lock lock;
    Condition cond;
    boolean running;

    public ThreadPanel() {
        super();
        createGUI();
        dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        lock = new ReentrantLock();
        cond = lock.newCondition();
        running = true;
    }

    public void createGUI() {
        setLayout(new BorderLayout());
        JScrollPane jsp = new JScrollPane(txt = new JTextArea(), JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        add(jsp, BorderLayout.CENTER);
        add(ppButton = new JButton(RUNNING), BorderLayout.SOUTH);
        ppButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {

                System.out.println(1);
                if (running) {
                    running = false;
                    ppButton.setText(SUSPENDED);
                } else {
                    running = true;
                    ppButton.setText(RUNNING);
                    lock.unlock();
                }
                lock.lock();
                if (!running) {
                    cond.signalAll();
                }

                lock.unlock();

            }
        });
    }

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (!running)
                    cond.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
            Calendar cal = Calendar.getInstance();
            txt.append("\n" + dateFormat.format(cal.getTime()));
            try {
                Thread.sleep((long) (Math.random() * 1001 + 500));
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(2);
            lock.unlock();
        }
    }

}

Solution

  • I see you want a button to start and stop the thread. So basically you need to in the actionPerformed(), acquire the lock, look up the state of things, manipulate the state, tell the waiting entity that something changed, and then release all your locks.

    The Runnable thread (for lack of a label) should remain mostly unchanged but should check the Condition within a loop to avoid the case where your signalAll() wakes and the Condition still is not symenticly true or false. (signal() and signalAll() are not guaranteed to be sync right after the lock is released, so 2 calls to actionPerformed() may have happened already).

        public void createGUI() {
            setLayout(new BorderLayout());
            JScrollPane jsp = new JScrollPane(txt = new JTextArea(), JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
            add(jsp, BorderLayout.CENTER);
            add(ppButton = new JButton(RUNNING), BorderLayout.SOUTH);
            ppButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    // This is where we acquire the lock to safely look at the state
                    lock.lock();
                    System.out.println(1);
                    // Manipulate the state
                    if (running) {
                        running = false;
                        ppButton.setText(SUSPENDED);
                    } else {
                        running = true;
                        ppButton.setText(RUNNING);
                    }
    
                    // Signal that this conditional changed (is either true or false now)
                    cond.signalAll();
                    // Release the lock so other entities can go forward
                    lock.unlock();
    
                }
            });
        }
    
        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    // This should block until this condition is true with a loop
                    while (!running)
                        cond.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                Calendar cal = Calendar.getInstance();
                txt.append("\n" + dateFormat.format(cal.getTime()));
                // No need to sleep()
                System.out.println(2);
                lock.unlock();
            }
        }
    
    }