Changing owner for ReentrantLock - java

So I wrote a little program which will move circles around and when they collide they will move opposite direction, however when I'm trying to delay the execution so they won't move around stupidly fast I get java.lang.IllegalMonitorStateException
Lock in canvasRender.java, creating an instance:
ReentrantLock renderLock = new ReentrantLock();
Method which will pause execution for a moment, so circles won't move around super fast.
publlic void delay(){
renderLock.unlock();
try { Thread.sleep(10); } catch (Exception e) {} ;
renderLock.lock();
}
then from another class where I create a window and add actionListener
public static void main(String[] args){
//Buttons and other elements
// ...
JButton start = new JButton("Start!");
createAndShowGUI();
}
In createAndShowGUI():
static void createAndShowGUI(){
//adding elements to panels
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start(); //this will set gameIsRunning variable to true and create models
while (gameIsRunning) {
//update(); //which has delay(); at the end of frame drawing
//but even if just put delay()
delay(); //still says exception
start.setEnabled(false); //while game is running button is unavailable
}
start.setEnabled(true);
}
});
}
In this case my lock is owned by the Thread main, but at the time when I click button 'Start!' current is Thread AWT-EventQueue-0, and so the program crashes. How to fix this issue? (or where am I silly?)

The problem is that you're calling renderLock.unlock() from AWT-EventQueue-0 after renderLock.lock() was called by main. The thread AWT-EventQueue-0 isn't allowed to call unlock() it, since it's not the thread that called lock() it in the first place.
You could probably simplify things by dropping ReentrantLock and just using synchronized.
I don't know the design of the rest of your program, but it seems to me that the contents of the while loop belong in a separate thread. You generally don't want to loop in a UI listener method (such as actionPerformed() in ActionListener) because it will freeze up the GUI.
One thing you could do is add an Object to synchronize on:
private static final Object LOCK = new Object()
Then move the game-updating logic to its own thread — something like this:
private static class GameThread extends Thread {
public GameThread() {
super("GameThread");
}
public void run() {
synchronized (LOCK) {
start();
while (gameIsRunning) {
update();
try {
// Try to sleep for 10 millis:
LOCK.wait(10);
} catch (InterruptedException ignored) { }
}
}
// Re-enable the button:
javax.swing.SwingUtilities.invokeLater(() -> start.setEnabled(true));
}
}
And you can change your ActionListener to simply disable the button and start a GameThread:
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start.setEnabled(false);
synchronized(LOCK) {
if(!gameIsRunning) {
new GameThread().start();
}
}
}
});
Any other code that checks or modifies the state of the game should also be enclosed within a synchronized (LOCK) block. And if update() modifies the GUI as well as the game state, then it probably needs to do so with SwingUtilities.invokeLater().
It might also make things more clear to rename start() to setupGame() and JButton start to JButton startButton.

Related

Leave Event Dispatch Thread entry ONLY on key press (Java)

I understand that it is important to use the Event Dispatch Thread for any changes to the interface in Java. However, I have no idea how I can manipulate these events to stop/continue/start. I want to refrain from moving on to the next line of main() (after the ones which put the Runnable in the EventQueue) until a certain key is pressed.
I put together an example for clarity. What I'd like to do here is spawn the JFrame, allow the user to move the box around with the arrow keys and then press Enter to cease the box-shifting operations, and ONLY then make the calculation at the end of main() and cause the answer to appear. I should be able to get 400, 500, 600, etc. As it is, the calculation is made immediately after the JFrame appears, so the answer is always 300.
I carved out a spot for whatever action should be bound to Enter; it's underneath the declarations for the actions bound to the arrow keys.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class EndTheShifter extends JFrame
{
private Color ourRectColor = new Color(28,222,144);
private int ourRectWidth = 50;
private int ourRectHeight = 50;
protected static Point ourRecLocation = new Point(100,100);
// Rectangle object can paint itself
public class Rectangle
{
protected void paint(Graphics2D g2d)
{
g2d.setColor(ourRectColor);
g2d.fillRect(ourRecLocation.x, ourRecLocation.y, ourRectWidth, ourRectHeight);
}
} // Rectangle class
// OurRectangle can create a Rectangle and call paint() on it
public class OurRectangle extends JPanel
{
private Rectangle capableRectangle;
public OurRectangle()
{
capableRectangle = new Rectangle();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
capableRectangle.paint(g2d);
g2d.dispose();
}
} // OurRectangle class
KeyStroke pressRight = KeyStroke.getKeyStroke("RIGHT");
KeyStroke pressLeft = KeyStroke.getKeyStroke("LEFT");
KeyStroke pressUp = KeyStroke.getKeyStroke("UP");
KeyStroke pressDown = KeyStroke.getKeyStroke("DOWN");
KeyStroke pressEnter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0);
OurRectangle recToWorkWith = new OurRectangle();
// Create InputMap and ActionMap
InputMap inputMap = recToWorkWith.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = recToWorkWith.getActionMap();
// Mapping Shortcut
protected void setTheAction(KeyStroke a, String b, Action c)
{
inputMap.put(a,b);
actionMap.put(b,c);
}
// Constructor!!!
public EndTheShifter()
{
add(recToWorkWith);
Action rightAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if(ourRecLocation.x != 600)
ourRecLocation.x += 50;
else
ourRecLocation.x = 100;
recToWorkWith.repaint();
}
};
Action leftAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if(ourRecLocation.x != 100)
ourRecLocation.x -= 50;
else
ourRecLocation.x = 600;
recToWorkWith.repaint();
}
};
Action downAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if(ourRecLocation.y != 600)
ourRecLocation.y += 50;
else
ourRecLocation.y = 100;
recToWorkWith.repaint();
}
};
Action upAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if(ourRecLocation.y != 100)
ourRecLocation.y -= 50;
else
ourRecLocation.y = 600;
recToWorkWith.repaint();
}
};
/*
Action enterAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
}
}
setTheAction(pressEnter,"enterAction",enterAction);
*/
setTheAction(pressRight,"rightAction",rightAction);
setTheAction(pressLeft,"leftAction",leftAction);
setTheAction(pressDown,"downAction",downAction);
setTheAction(pressUp,"upAction",upAction);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800,800);
setVisible(true);
}
// Main kicks things off by putting all of the above
// in the Event Dispatch thread
// On an enter press, I want the last line of main() to run
public static void main(String[] argv)
{
EventQueue.invokeLater(
new Runnable()
{
#Override
public void run()
{
new EndTheShifter();
}
});
// What I want to trigger only on Enter
System.out.println(ourRecLocation.x + 2*ourRecLocation.y);
}
} // EndTheShifter, our outermost class
and ONLY then make the calculation at the end of main()
That is not the way a GUI works.
The main() method is only used to display the frame.
Once the frame is visible the EDT is started and the frame sits there waiting for user events to be generated.
Your application code then responds to these user events.
I understand that it is important to use the Event Dispatch Thread for any changes to the interface in Java.
All code invoked in a listener does execute on the EDT. So the code in your Action does execute on the EDT. You don't need to do anything special.
What I want to trigger only on Enter
Then that logic should be contained in the Enter Action.
I would like to support what camickr said; there is likely a better way to achieve what you are trying to do. That said, if you really want to make your main method wait until the enter key is pressed, here's how:
First, at the top of your file, define an object to use as a synchronization lock like so:
public static final Object LOCK = new Object();
Then, in your main method, before your println statement, put the following code:
synchronized (LOCK) {
LOCK.wait();
}
What this does is it waits until the LOCK object's monitor lock is not being used by any thread (very simplified explanation, read more here), and then it makes the current thread (in this case, the thread that started your main method) wait indefinitely.
Next, add a throws declaration to the method header on your main method:
public static void main(String[] argv) throws InterruptedException
This tells the compiler that your code could throw an InterruptedException, which would happen if your thread was interrupted while it was waiting.
Finally, anywhere in your EndTheShifter constructor, put the following code:
synchronized (LOCK) {
LOCK.notify();
}
This again waits until the LOCK object's monitor lock becomes available, and it then "notifies" all threads waiting on the LOCK object that they may continue. In this case, it will make our main thread continue and execute the println.

How to implement a smoothly falling JLabel without using Timer, but Threads instead

Alright, so I have a null layout JPanel with a single JLabel in it. The JLabel is positioned at (0,0). What I'm trying to do is use a while loop in a new Thread to sleep the new Thread and then shift the JLabel 10px down by using SwingUtilities.invokeLater . The problem is that the UI gets updated in a laggy sort of way. It doesn't update every time it should, but skips lots of updates and shifts in big chunks. I know I can easily do it with Timer, but the point is understanding Threads better. Thanks in advance!
Code:
private void start(){
Updater up = new Updater();
up.start();
}
public void updatePosition(){
int y = label1.getLocation.y;
label.setBounds(0,y+10, 10,10);
}
private class Updater extends Thread{
public void run(){
while(!shouldQuit){
try{
Updater.sleep(100);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
updatePosition();
}
});
}catch(Exception e){
System.out.println(e);
}
}
}
}
EDIT:
I got it to work by replacing the while loop with a call to a new Thread instance in the updatePosition() method, to settle things down a bit. And also, it wasn't only the Thread that was causing the problem, so I had to force the panel to re-layout it's subviews by calling revalidate() on it.
Here's how it looks (the fully working one):
private void start(){
new Updater().start();
}
public void updatePosition(){
int y = label1.getLocation.y;
label.setBounds(0,y+10, 10,10);
panel.revalidate();
if(!shouldQuit) new Updater().start();
}
private class Updater extends Thread{
public void run(){
try{
Updater.sleep(100);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
updatePosition();
}
});
}catch(Exception e){
System.out.println(e);
}
}
}
You should try to use visibility and GridLayout to maximize movement. You can use a int var to count threads and reciprocate that to the label. As well, you should be using your ability o create Updaters, more and smoother. Just do the start() mwthod while trolling for threads :-)
You could have something besides an infinity call to start. I think you've lost the inheritance from the class, itself. The object label1 must ave been lost in tbe fray. If that's not it, then I'm pretty sure I'm not really able to answer this one.

button event doesn't work properly

I have some doubts about the use of the methods wait() and notify(). I have the next code which has some button events, the first time the user pushes the button it has to stop printing, and the second time it restarts printing again. I understand that is better to use Runnable instead of Thread, but I have to use Thread because of the requirements. The code works fine the first time the button is pushed but the second time it doesn´t, I want to use the wait() and the notify, but i don´t know how to do it with this particular code.
class Thr extends Thread{
private int count = 0;
private long pause;
private boolean canPrint = true;
private JTextArea textArea;
Thr(long miliseconds,JTextArea text){
pause = miliseconds;
textArea = text;
}
public void pushedButton(){
if(canPrint)
this.canPrint = false;
else
this.canPrint = true;
}
public void run()
{
while(this.canPrint)
{
try
{
this.printCounter();
Thread.sleep(pause);
this.count++;
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
public void printCounter(){
String time;
time = Integer.toString(count);
textArea.setText(time);
}
}
class Interface extends JFrame implements ActionListener{
private JTextArea textArea,textArea2;
private JButton button;
private Thr thread,threadEvent;
Interface()
{
textArea = new JTextArea(10,7);
textArea2 = new JTextArea(10,7);
thread = new Thr(2000,textArea);
threadEvent = new Thr(1000,textArea2);
button = new JButton("Pausar/Reanudar");
this.getContentPane().add(button,BorderLayout.SOUTH);
this.getContentPane().add(textArea,BorderLayout.WEST);
this.getContentPane().add(textArea2,BorderLayout.EAST);
thread.start();
threadEvent.start();
button.addActionListener(this);
}
public void actionPerformed(ActionEvent event)
{
threadEvent.pushedButton();
}
}
public class MensajesHilos {
public static void main(String[] args){
Interface i = new Interface();
i.setTitle("Control Threads");
i.setBounds(200, 200, 300, 240);
i.setVisible(true);
}
}
The way you have coded, if you want to achieve the desired result,
I feel the modification need to be done in run method,
public void run()
{
while(true)
{
if(this.canPrint){
try
{
this.printCounter();
Thread.sleep(pause);
this.count++;
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
in this way, your Thread will never go dead and toggle printing based on canPrint boolean value.
Also, make sure to declare canPrint variable volatile, so that changes to it will be directly written to main memory and reflected immediately.
"button event doesn´t work properly"
This is false, if you put a print statement in the actionPerformed method, you will see that it is called every time you press the button.
By the way note that you can simplify this
if(canPrint)
this.canPrint = false;
else
this.canPrint = true;
To
this.canPrint = !this.canPrint;
Note that it is a good practice to always put #Override anotation on top of overriden method.
#Override
public void actionPerformed(ActionEvent event)
{
threadEvent.pushedButton();
}
Now why don't you get the expected result ?
You ommit to call thread.pushedButton, so the canPrint will only be reseted in the threadEvent object, and will never be in thread.
Note that once the boolean are set to false, you will exit the loop and the process won't start back after even if you re-set the boolean value to true. This example will works using while(true) however, you should change the true to any sentinel value to handle the exit of the program as this will loop forever.
#Override
public void run()
{
while(true)
{
if(this.canPrint)
{
this.printCounter();
this.count++;
}
try
{
Thread.sleep(pause);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Also, make sure that pause is never 0 else you will eat all of the computer process.
Note that, as other stated, you should declare variables that are accessed in thread as volatile (canPrint) in your case.

How do I run a terminate command on JFrame exit?

I know the basics behind trying to write code that terminates a thread, but I've run into a bit of a problem.
I've got a JButton in a JFrame GUI that launches the simulation I'm trying to animate. It is called in the ActionPerformed code of the JButton by
new AnswerWorker().execute();
The AnswerWorker class, in turn, extends SwingWorker so that the animation frame can be drawn while the GUI is still active.
public class AnswerWorker extends SwingWorker<String, Integer> {
protected String doInBackground() throws Exception
{
Threading threading = new Threading();
return null;
}
protected void done()
{
try {
JOptionPane.showMessageDialog(InputGUI.this, AMEC.unsuccesfulpercentage + "% of iterations had trucks that had to sleep over");
AMEC.unsuccesfulpercentage = 0;
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
To create a way of stopping the simulation thread, I created the class Threading, that calls the function that runs the simulation.
public class Threading extends Thread {
#Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
AMEC.runsimulation();
} catch (IOException ex) {
Logger.getLogger(InputGUI.class.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(InputGUI.class.getName()).log(Level.SEVERE, null, ex);
}
}
return;
}
}
Now, in the runsimulation() function, I initialize a JFrame, and I want to terminate the thread running the simulation if the JFrame is closed by clicking on its close button. How do I do this?
EDIT: all of the above code is called in a file InputGUI.java that contains all my GUI elements. The runsimulation function is in my main project file, AMEC.java
You could override the dispose() method on the JFrame to include a call to stop the thread
#Override
dispose(){
stopThread();
super.dispose();
}
How do I run a terminate command on JFrame exit?
add WindowListener to JFrame, from windowClosing event you have to call JFrame.setVisible(false) and then to call SwingWorker
after SwingWorker ended to terminate current JVM, to show a JOptionPane in the case that any exception raised, or to show JFrame again back to screen, required to change DefaultCloseOperation, to HIDE_ON_CLOSE

Updating the JPanel in a JFrame

I have a JFrame with a CardLayout component. I am using the CardLayout to switch between different JPanel's at different moments of the application execution. At some point I am using a SwingWorker Object to generate some XML files. In this time I want to display another JPanel in my window to tell the user to wait. On this JPanel I want to switch between 3 labels.
JLabel 1 would be : "Please wait."
JLabel 2 would be : "Please wait.."
JLabel 3 would be : "Please wait..."
Right now the code looks like this:
ConvertersWorker.execute();
CLayout.show(Cards, WAIT_PANEL);
Timer t =new Timer(500, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e) {
WaitPanel.SwitchLabels();
}
});
t.start();
while (!this.finishedConverting)
{
}
//After this im am executing other methods based on the generated XML files
The SwingWorker code:
SwingWorker<Boolean, Void> ConvertersWorker = new SwingWorker<Boolean, Void>() {
public Boolean doInBackground() {
Boolean result = RunConverters();
return result;
}
public void done() {
try {
finishedConverting = get();
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (ExecutionException ex) {
ex.printStackTrace();
}
}
} ;
The second JPanel is not even displayed because the JFrame blocks. It blocks because of the while loop but I don't know how to implement it differently. And also the method done() from the SwingWorker is never executed. If it were executed then the finishedConverting variable would have been set to true and the while loop would have stopped. Can anyone help me to find a better solution?
I know you solved this but that's happening because you are using just one thread, and it blocked because of the While, so you need to create a new thread to handle this
new Thread(){
public void run() {
//code here
}
}.start();
and to refresh the content of a JPanel you can use
myJpanel.doLayout();
or
myJpanel.repaint();
I removed the while loop and moved the code which was after the loop in another method which is executed in the done() method of the SwingWorker so now it works.

Categories