Reputation: 18068
I have looked everywhere. How to suspend/pause it by code until I call it to awake using any java.util.concurrent
methods/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
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();
}
}
}
Upvotes: 2
Views: 872
Reputation: 10184
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();
}
}
}
Upvotes: 1
Reputation: 8152
One way to do what I think you're asking for is to use a CyclicBarrier (from java.util.concurrent), parameterising it with two "parties".
The first thread to call await
on the barrier will be suspended/blocked until a second thread also calls await
, at which point both threads can proceed.
Here's a simple code sample:
import java.util.concurrent.CyclicBarrier;
public class Test {
public static void main(String[] args) {
// a barrier requiring two threads to call await before
// any thread can proceed past the barrier
final CyclicBarrier barrier = new CyclicBarrier(2);
new Thread(){
@Override
public void run() {
try {
// do some stuff
System.out.println("in thread, before the barrier");
// calling await blocks until two threads
// (this one and one other) have called await
barrier.await();
// do some more stuff
System.out.println("in thread, after the barrier");
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
try {
System.out.println("main thread, before barrier");
// calling await blocks until two threads
// (this one and one other) have called await
barrier.await();
System.out.println("main thread, after barrier");
} catch (Exception exc) {
exc.printStackTrace();
}
}
}
Upvotes: 1