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();
}
}
}
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();
}
}
}
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();
}
}
}
Related
Here is my code... How can I make it work so that it runs the loop while the user is holding a button and stops when the user releases the button?
public void nextPrimeNum()
{
x = false;
int b = 2;
ArrayList<Integer> next = new ArrayList<Integer>();
while(x)
{
next = factors(b);
if(next.size()==2)
{
System.out.println(b);
}
b++;
}
System.out.println("End");
}
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == 401)
{
x = true;
}
}
public void keyRealesed(KeyEvent e)
{
if(e.getKeyCode() == 402)
{
x = false;
}
}
GUI and multi-thread programming is inherently difficult.
So, this is as simple as it could be, without violating best practices too much.
You need several things:
A separate Thread for printing primes:
Its run method loops for ever, but pauses when the Space key is not pressed.
(see Defining and Starting a Thread for more info)
A KeyListener which will be called from AWT's event dispatch thread:
The event handling methods are designed to finish fast, so that other events
(like moving, resizing and closing the frame) still are handled fast.
(see How to Write a Key Listener
and The Event Dispatch Thread for more info)
A visible GUI component (JFrame) for adding the KeyListener
Some synchronization between the 2 threads (via synchronized, notify and wait)
so that the prime-printing starts/continues on keyPressed
and suspends on keyReleased
(see Guarded Blocks for more info)
Initialize and start the whole GUI by invoking initGUI.
(see Initial Threads for more info)
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class Main implements Runnable, KeyListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(Main::initGUI);
}
private static void initGUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JLabel("Press SPACE key for printing primes"));
frame.pack();
frame.setLocationRelativeTo(null); // center on screen
frame.setVisible(true);
Main main = new Main();
frame.addKeyListener(main);
Thread thread = new Thread(main);
thread.start();
}
private boolean spaceKeyPressed;
private boolean isPrime(int n) {
for (int i = 2; i < n; i++) {
if (n % i == 0)
return false;
}
return true;
}
#Override
public void run() {
for (int n = 2; /**/; n++) {
while (!spaceKeyPressed) {
synchronized (this) {
try {
wait(); // waits until notify()
} catch (InterruptedException e) {
// do nothing
}
}
}
if (isPrime(n)) {
System.out.println(n);
}
}
}
#Override
public void keyTyped(KeyEvent e) {
// do nothing
}
#Override
public synchronized void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
spaceKeyPressed = true;
notifyAll(); // cause wait() to finish
}
}
#Override
public synchronized void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
spaceKeyPressed = false;
notifyAll(); // cause wait() to finish
}
}
}
So, the answer is - it's complicated. It covers broad topics such as concurrency (in general), GUI development, best practices with the specific API (Swing) which are better covered in more detail by reading through the various tutorials (and experimenting)
Concurrency
Creating a GUI With JFC/Swing
Concurrency in Swing
Worker Threads and SwingWorker
How to Use Actions
How to Use Key Bindings
The example presents two ways to execute the "loop" (which is presented in the doInBackground method of the CalculateWorker class).
You can press and hold the JButton or press and hold the [kbd]Space[kbd] bar, both will cause the "main loop" to run, updating the JTextArea with the results...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextArea ta;
private CalculateWorker worker;
public TestPane() {
setLayout(new BorderLayout());
ta = new JTextArea(20, 20);
ta.setEditable(false);
add(new JScrollPane(ta));
worker = new CalculateWorker(ta);
JButton btn = new JButton("Press");
btn.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
System.out.println("...isRunning = " + worker.isRunning());
if (!worker.isRunning()) {
return;
}
System.out.println("...isPressed = " + btn.getModel().isPressed());
System.out.println("...isPaused = " + worker.isPaused());
if (btn.getModel().isPressed()) {
worker.pause(false);
} else {
worker.pause(true);
}
}
});
add(btn, BorderLayout.SOUTH);
worker.execute();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "Space.released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), "Space.pressed");
am.put("Space.released", new CalculateAction(false, worker));
am.put("Space.pressed", new CalculateAction(true, worker));
}
public class CalculateWorker extends SwingWorker<List<String>, String> {
private AtomicBoolean run = new AtomicBoolean(true);
private AtomicBoolean paused = new AtomicBoolean(false);
private ReentrantLock pausedLocked = new ReentrantLock();
private Condition pausedCondition = pausedLocked.newCondition();
private JTextArea ta;
public CalculateWorker(JTextArea ta) {
this.ta = ta;
pause(true);
}
public void stop() {
run.set(false);
pausedLocked.lock();
pausedCondition.signalAll();
pausedLocked.unlock();
}
public void pause(boolean pause) {
paused.set(pause);
pausedLocked.lock();
pausedCondition.signalAll();
pausedLocked.unlock();
}
public boolean isPaused() {
return paused.get();
}
public boolean isRunning() {
return run.get();
}
#Override
protected List<String> doInBackground() throws Exception {
List<String> values = new ArrayList<>(256);
long value = 0;
System.out.println("!! Start running");
while (run.get()) {
while (paused.get()) {
System.out.println("!! I'm paused");
pausedLocked.lock();
try {
pausedCondition.await();
} finally {
pausedLocked.unlock();
}
}
System.out.println("!! Start loop");
while (!paused.get() && run.get()) {
value++;
values.add(Long.toString(value));
publish(Long.toString(value));
Thread.sleep(5);
}
System.out.println("!! Main loop over");
}
System.out.println("!! Run is over");
return values;
}
#Override
protected void process(List<String> chunks) {
for (String value : chunks) {
ta.append(value);
ta.append("\n");
}
ta.setCaretPosition(ta.getText().length());
}
}
public class CalculateAction extends AbstractAction {
private boolean start;
private CalculateWorker worker;
public CalculateAction(boolean start, CalculateWorker worker) {
putValue(NAME, "Calculate");
this.start = start;
this.worker = worker;
}
#Override
public void actionPerformed(ActionEvent e) {
worker.pause(start);
}
}
}
}
Is there a simpler solution?
Of course, I always go for the most difficult, hard to understand solutions first (sarcasm)
While it "might" be possible to reduce the complexity, the example presents a number of "best practice" concepts which you would do well to learn and understand.
The solution could also be done differently depending on the API used, so, it's the "simplest" solution for the specific API choice.
I wanted to do it from the console!
Java can't do that - it's console support is rudimentary at best and doesn't support a concept of "key pressed/released" actions (since it's running in a single thread, it would be impossible for it to do otherwise).
There "are" solutions you might try, but they would require a third party library linked to native binaries to implement, which would (possibly) reduce the number of platforms it would run on
sorry but it's the first time i use Threads.
i want Parlami class thread to sleep and be awaken only by the actionListener.
I tried this way but it isn't working, he still sleeps.
Is it right to use thread this way or should i use wait() ?
package parlami;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* #author giacomofava
*/
public class Parlami
{
public boolean finito = false;
public String s="";
public void ascolta()
{
int i=0;
while (i<=1500)
{
// dormi 50 millisecondi
try
{
Thread.sleep(50);
i+=40;
}
catch (InterruptedException e)
{
}
while (voce.SpeechInterface.getRecognizerQueueSize() > 0)
{
s = s+"\n"+voce.SpeechInterface.popRecognizedString();
}
}
}
public String scrivi()
{
return "Hai detto: "+s;
}
public void leggi()
{
voce.SpeechInterface.synthesize(s);
}
public void dormi(int milli)
{
try
{
System.out.println("i'm sleeping");
Thread.sleep(milli);
}
catch (InterruptedException ex)
{
System.out.println("i'm awake ");
ascolta();
}
}
}
this is the gui:
public class GUI extends JFrame
{
private Parlami p;
private JPanel nord, centro;
private JButton registra, leggi;
private JTextArea display;
public static void main(String[] args)
{
new GUI();
}
public GUI()
{
p=new Parlami();
initComponents();
}
private void initComponents()
{
voce.SpeechInterface.init("./lib", true, true,"./lib/gram", "vocabolario");
// N O R D
nord=new JPanel();
display=new JTextArea("");
display.setForeground(Color.GREEN);
display.setBackground(Color.BLACK);
nord.setBackground(Color.BLACK);
nord.add(display);
// C E N T R O
centro=new JPanel();
registra=new JButton("tieni premuto per registrare");
registra.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
Thread.currentThread().interrupt();// <-------- HERE I TRY TO AWAKE HIM
display.setText(p.scrivi());
}
});
centro.add(registra);
leggi=new JButton("leggi");
centro.add(leggi);
this.setLayout(new BorderLayout());
this.add(nord, BorderLayout.NORTH);
this.add(centro, BorderLayout.CENTER);
this.setSize(700,300);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
p.dormi(50000); // <-------- HERE I TELL HIM TO SLEEP
}
}
If you call Thread.sleep on the Swing event thread, you will put the entire application to sleep rendering it useless, but more importantly, there's no need to do this. You simply have the ActionListener activate whichever object needs activation as this is how event-driven programming works.
If you need a delay in a Swing application, use a Swing Timer, something that has been discussed over and over again on this site.
This is a basic concept of thread wait/notify associated with the topic of thread locks. Basically, you have some common object which is acting as the "lock", one thread "waits" on this thread and when another thread needs to, it "notifies" the monitors that some action has occurred to which they should/can respond.
It'd start by having a look at Lock Objects for more details.
Below is a very basic example of the concept, a Thread is allowed to run continuously, but which "waits" on the common lock. The ActionListener of the button "notifies" the lock when it is pressed, allowing the Thread to continue working until, once again, blocks at the "wait"
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Thread t = new Thread(new Runner());
t.start();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static final Object LOCK = new Object();
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
JButton btn = new JButton("Press me");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
synchronized (LOCK) {
LOCK.notifyAll();
}
}
});
add(btn);
}
}
public class Runner implements Runnable {
#Override
public void run() {
while (true && !Thread.currentThread().isInterrupted()) {
synchronized (LOCK) {
try {
System.out.println("Nothing to see here, just waiting");
LOCK.wait();
} catch (InterruptedException ex) {
}
}
System.out.println("Look at me, I'm busy");
}
}
}
}
Remember, Swing is single threaded, never perform any action which is blocking within the context of the Event Dispatching Thread, equally, never update the UI from outside the EDT.
If you need to update the UI for some reason from the other thread, then I suggest you have a look at SwingWorker, which will make your life much simpler. See Worker Threads and SwingWorker for more details.
You have an ActionListener which is notified when the button is activated, why do you need a monitor lock to perform the associated action? Does it take a noticeable amount of time to start the required action? You could just start a new thread when the button is clicked.
If you're waiting for some kind of timeout, then, to be honest, a Swing Timer is probably more suited to the task
This question already has answers here:
Stopping a Thread in Java? [duplicate]
(3 answers)
Closed 9 years ago.
I am trying to create a thread for a specific task to run in another class. The thread is starting the task but when I try to stop the thread, it is not stopping. The thread continues till the loop. Could you please help me out.
Thread Class:
package com.development;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ThreadExample extends JFrame {
MyThread mt;
Thread th;
ThreadExample(){
JPanel p1 = new JPanel();
p1.setPreferredSize(new Dimension(400,400));
JButton b1 = new JButton("Start");
JButton b2 = new JButton("Stop");
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
mt = new MyThread();
th = new Thread(mt);
th.start();
}
});
b2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
th.interrupt();
}
});
p1.add(b1);
p1.add(b2);
this.getContentPane().add(p1);
this.pack();
this.setVisible(true);
}
public static void main(String arg[]) {
ThreadExample mt = new ThreadExample();
mt.setVisible(true);
}
public class MyThread implements Runnable{
private volatile boolean runnable=true;
DisplayMsg dm = new DisplayMsg("Cycle");
#Override
public void run() {
while(!Thread.currentThread().isInterrupted()) {
// TODO Auto-generated method stub
dm.show();
}
}
}
}
DisplayMsg class:
package com.development;
public class DisplayMsg {
private String dispMsg;
private boolean runnable;
DisplayMsg(String dispMsg){
this.dispMsg=dispMsg;
}
public void show() {
for(int t=0;t<100;t++) {
try {
System.out.println(dispMsg + t);
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Your DisplayMsg class loops for 100 seconds, and ignores interrupts. Indeed, when Thread.sleep() is interrupted, it resets the interrupt status, then throws an InterruptedException. Since you ignore the InterruptedException, the thread continues as if nothing happened.
public void show() {
for(int t=0;t<100;t++) {
try {
System.out.println(dispMsg + t);
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Don't ignore interrupts:
public void show() {
for(int t=0;t<100;t++) {
try {
System.out.println(dispMsg + t);
Thread.sleep(1000);
} catch (InterruptedException e) {
// re-interrupt the thread and stop looping
Thread.currentThread().interrupt();
return;
}
}
}
A much simpler design which achieves what you need is the following:
public class MyThread implements Runnable{
DisplayMsg dm = new DisplayMsg("Cycle");
#Override public void run() {
try { while(true) dm.show(); }
catch (InterruptedException e) { }
}
}
public class DisplayMsg {
...
public void show() throws InterruptedException {
for(int t=0;t<100;t++) {
System.out.println(dispMsg + t);
Thread.sleep(1000);
}
}
}
This will simply let the InterruptedException propagate and end MyThread, with no effort on your own.
When you catch the InterruptedException in your DisplayMsg class, the interrupted flag is reset.
It's fine to catch the exception, but if you need to subsequently know if the thread has been interrupted, you need to reset the flag by interrupting again with Thread.currentThread.interrupt(); in that catch block and break out of the loop / return.
I believe what is happening is that you need to be checking !Thread.currentThread().isInterrupted() before each sleep. You are calling show() in the method which will iterate for 100 seconds only then will the your while loop check to see if the thread is interrupted. Move your check for interrtuped to your show method and see if it sees the interrupted flag. As per the next answers you should also set the Thread interrupted flag when you catch the InterruptedException.
I need to make a method that returns only when a JButton is pressed. I have a custom JButton class
public class MyButton extends JButton {
public void waitForPress() {
//returns only when user presses this button
}
}
and I want to implement waitForPress. Basically, the method should only return when the user presses the button with their mouse. I have achieved similar behavior for JTextField (to return only when user presses Space):
public void waitForTriggerKey() {
final CountDownLatch latch = new CountDownLatch(1);
KeyEventDispatcher dispatcher = new KeyEventDispatcher() {
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_SPACE) {
System.out.println("presed!");
latch.countDown();
}
return false;
}
};
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(dispatcher);
try {
//current thread waits here until countDown() is called (see a few lines above)
latch.await();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(dispatcher);
}
but I would like to do the same thing with JButton.
In advance: Please, if you wish to comment saying that this is not a good idea and that one should simply wait for actionPerformed event on a JButton and then do some action, please realize I already know that and have a good reason for doing what I'm asking here. Please try to only help with what I've asked. Thanks!!
In advance: Please, also realize that implementing actionPerformed also will not directly solve the problem. Because the code will progress even without the button being pressed. I need the program to stop, and only return when the button has been pressed. Here is a terrible solution if I were to use actionPerformed:
public class MyButton extends JButton implements ActionPerformed {
private boolean keepGoing = true;
public MyButton(String s) {
super(s);
addActionListener(this);
}
public void waitForPress() {
while(keepGoing);
return;
}
public void actionPerformed(ActionEvent e) {
keepGoing = false;
}
}
For what it's worth, here is how you can do it with wait() and notify() but yet I feel that there is a deeper problem here. I would not consider this as a satisfying solution:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class TestBlockingButton {
boolean clicked = false;
private Object toNotify;
private void initUI() {
JFrame frame = new JFrame(TestBlockingButton.class.getSimpleName());
JButton button = new JButton("Click me");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
clicked = true;
if (toNotify != null) {
synchronized (TestBlockingButton.this) {
toNotify.notify();
}
}
}
});
frame.add(button);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void waitForProcess() {
toNotify = this;
while (!clicked) {
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
System.out.println("continuing work");
}
public static void main(String[] args) {
final TestBlockingButton test = new TestBlockingButton();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
test.initUI();
}
});
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
pool.execute(new Runnable() {
#Override
public void run() {
System.out.println("I was doing something and now I will wait for button click");
test.waitForProcess();
System.out.println("User has now cliked the button and I can continue my work");
}
});
}
}
As you asked for an implementation with a mutex, here's what it would be like.
I'm using an ActionListener though, but there's no busy wait in it. If that isn't what you desire, you atleast saw what Burkhard meant ;)
public class MyButton extends JButton implements ActionListener
{
private Semaphore sem = new Semaphore(1);
public MyButton(String text) throws InterruptedException
{
super(text);
addActionListener(this);
sem.acquire();
}
#Override
public void actionPerformed(ActionEvent e) {
sem.release();
}
public void waitForPress() throws InterruptedException {
sem.acquire();
//do your stuff
sem.acquire();
//or just
//waitForPress()
//if you dont want it to end.
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame();
MyButton m = new MyButton("test");
frame.add(m);
frame.pack();
frame.setVisible(true);
m.waitForPress();
//another time, if you only want it to block twice
m.waitForPress();
}
}
But I don't think this is a clean approach, but it doesn't consume CPU-time like a while(isStatementTrue)-implementation.
An important thing here is: you're blocking the main thread with m.waitForPress() but as you wrote you're quite experienced and you know how to handle that.
i use from a class that extended from jframe and it has a button(i use from it in my program)
i want when run jframe in my program the whole of my program pause
until i press the button.
how can i do it
in c++ getch() do this.
i want a function like that.
Pausing Execution with Sleep, although I doubt that is the mechanism that you'll want to use. So, as others have suggested, I believe you'll need to implement wait-notify logic. Here's an extremely contrived example:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
#SuppressWarnings("serial")
public class PanelWithButton extends JPanel
{
// Field members
private AtomicBoolean paused;
private JTextArea textArea;
private JButton button;
private Thread threadObject;
/**
* Constructor
*/
public PanelWithButton()
{
paused = new AtomicBoolean(false);
textArea = new JTextArea(5, 30);
button = new JButton();
initComponents();
}
/**
* Initializes components
*/
public void initComponents()
{
// Construct components
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
add( new JScrollPane(textArea));
button.setPreferredSize(new Dimension(100, 100));
button.setText("Pause");
button.addActionListener(new ButtonListener());
add(button);
// Runnable that continually writes to text area
Runnable runnable = new Runnable()
{
#Override
public void run()
{
while(true)
{
for(int i = 0; i < Integer.MAX_VALUE; i++)
{
if(paused.get())
{
synchronized(threadObject)
{
// Pause
try
{
threadObject.wait();
}
catch (InterruptedException e)
{
}
}
}
// Write to text area
textArea.append(Integer.toString(i) + ", ");
// Sleep
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
}
}
}
}
};
threadObject = new Thread(runnable);
threadObject.start();
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(400, 200);
}
/**
* Button action listener
* #author meherts
*
*/
class ButtonListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent evt)
{
if(!paused.get())
{
button.setText("Start");
paused.set(true);
}
else
{
button.setText("Pause");
paused.set(false);
// Resume
synchronized(threadObject)
{
threadObject.notify();
}
}
}
}
}
And here's your main class:
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class MainClass
{
/**
* Main method of this application
*/
public static void main(final String[] arg)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new PanelWithButton());
frame.pack();
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
});
}
}
As you can see, this example application will continually write to the text area until you click the button that reads 'Pause', whereupon to resume you'll need to click that same button which will now read 'Start'.
You don't say what you mean by pause. What is your app doing?
As a rule of thumb you CAN'T pause a UI app. User interface applications run from a message processing loop. Message comes in, message is dispatched, loop waits for another message. An app still needs to handles things like the user clicking on buttons, resizing the window, closing the app and so forth so this loop runs continuously.
If you want your application to "pause" in the sense of prevent the user doing something, just grey out whatever button or menu it is you don't want users to be doing.
If your app is running a thread in the background and wish it to suspend that action until you resume it, you can do so fairly easily like this.
MyThread mythread = new MyThread();
// Main thread
void pause() {
mythread.pause = true;
}
void resume() {
synchronized (mythread) {
mythread.pause = false;
mythread.notify();
}
}
class MyThread extends Thread {
public boolean pause = false;
public void run() {
while (someCondition) {
synchronized (this) {
if (pause) {
wait();
}
}
doSomething();
}
}
}
It is also possible to use Thread.suspend(), Thread.resume() to accomplish similar but these are inherently dangerous because you have no idea where the thread is when you suspend it. It could have a file open, be half way through sending a message over a socket etc. Putting a test in whatever loop controls your thread allows you do suspend at a point when it is safe to do so.
This answer entirely depends on whether I understand your question correctly, please give a bit more info if you want better answers. Here goes:
Pausing in a loop scenario
boolean paused;
while(true ) {
if(paused)
{
Thread.sleep(1000); // or do whatever you want in the paused state
} else {
doTask1
doTask2
doTask3
}
}
Threads:
You can also put those tasks into a seperate thread and not on the GUI thread which is typically what you would do for long running operations.
Pausing a thread is very easy. Just call suspend() on it. When you want to unpause call resume(). These methods however are dangerous and have been deprecated. Better or rather safer way to do it would be similar to the above by checking a pause flag.Here is a short example I had lying around in my snippets. Cant exactly remember where I got it in the first place:
// Create and start the thread
MyThread thread = new MyThread();
thread.start();
while (true) {
// Do work
// Pause the thread
synchronized (thread) {
thread.pleaseWait = true;
}
// Do work
// Resume the thread
synchronized (thread) {
thread.pleaseWait = false;
thread.notify();
}
// Do work
}
class MyThread extends Thread {
boolean pleaseWait = false;
// This method is called when the thread runs
public void run() {
while (true) {
// Do work
// Check if should wait
synchronized (this) {
while (pleaseWait) {
try {
wait();
} catch (Exception e) {
}
}
}
// Do work
}
}
} // Create and start the thread
MyThread thread = new MyThread();
thread.start();
while (true) {
// Do work
// Pause the thread
synchronized (thread) {
thread.pleaseWait = true;
}
// Do work
// Resume the thread
synchronized (thread) {
thread.pleaseWait = false;
thread.notify();
}
// Do work
}
class MyThread extends Thread {
boolean pleaseWait = false;
// This method is called when the thread runs
public void run() {
while (true) {
// Do work
// Check if should wait
synchronized (this) {
while (pleaseWait) {
try {
wait();
} catch (Exception e) {
}
}
}
// Do work
}
}
}
Hope this helps
try my java pause button:
package drawFramePackage;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.Timer;
public class Milliseconds2 implements ActionListener, MouseListener{
JFrame j;
Timer t;
Integer onesAndZeros, time, time2, placeHolder2;
Boolean hasFired;
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
new Milliseconds2();
}
public Milliseconds2(){
j = new JFrame();
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
j.setSize(new Dimension(300, 300));
j.setVisible(true);
j.addMouseListener(this);
onesAndZeros = new Integer(0);
time = new Integer(0);
time2 = new Integer(0);
placeHolder2 = new Integer(0);
hasFired = new Boolean(true);
t = new Timer(2400, this);
time = (int) System.currentTimeMillis();
t.start();
}
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
if (onesAndZeros.equals(0)){
t.stop();
if (hasFired){
time2 = t.getDelay() - ((int) System.currentTimeMillis() - time);
}
else{
time2 -= (int) System.currentTimeMillis() - placeHolder2;
}
if (hasFired){
hasFired = false;
}
onesAndZeros = -1;
}
if (onesAndZeros.equals(1)){
//System.out.println(time2);
t.setInitialDelay(time2);
t.start();
placeHolder2 = (int) System.currentTimeMillis();
onesAndZeros = 0;
}
if (onesAndZeros.equals(-1)){
onesAndZeros = 1;
}
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
time = (int) System.currentTimeMillis();
hasFired = true;
System.out.println("Message");
}
}
Freezing your Main Thread will effectively freeze the entire program and could cause the operating system to think the application has crashed, not quite sure so correct me if I'm wrong. You could try to hide/disable the controls and enable them again when the user clicks on your button.
UI performs task using message driven mechanism.
If you have a button in your UI and you want to run something when that button is pressed, you should add an object of ActionListener to your button. Once the button is pressed, it fires the ActionListener object to perform a task, e.g.:
button.addActionListener(new ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// do something
}
});
If you want to stop something when you press a pause button, you will defnitely need a Thread. This is more complicated than the former case.