I've searched for a solution for my problem all over but I cannot find anything close. Here is my problem: I have just started to learn Swing in Java and I have an application that will click randomly between a min and max amount of time. The user enters a min and max time and clicks a button which starts this code:
class CalcButtonListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent arg0) {
//get data from text fields and store as integers in milliseconds.
//create a robot and random number between min and max
while(run == true){
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
robot.delay(num.nextInt(max-min+1)+min);
}
}
}
}
I've removed some code because it wasn't relevant to the issue.
The problem is I have another button to set the variable run to false but once the first button is clicked, I can't click the second button or the exit button at the top for that matter.
I'm sure my mistake is a very basic one but I can't see it.
Thank you for any replies that help me better understand the subject.
Edit: Changed the while loop from "while (run = true)" to "while (run == true)".
while(run == true){
"=" sets run to true
"==" compares the value of run to true
Also you can just use
while(run){
Take a look at SwingWorker. And just do while(run)
Every event will be processed by a single thread called Event Dispatch thread(EDT). If you have an infinite call inside one of the events, EDT cannot process the next event in the event queue.
UPDATE
This answer is updated, because #uckelman pointed me out that, with the condition run = true, the stop button never breaks the while loop, because it's needed to change to run = false within the loop. Then I post a simple and alternative solution to this logic problem, to schedule a task repeatedly with a timer. For details, please check this SO question.
About the events for the buttons: if you have two buttons, one to start a loop and one to end the loop, just try this code:
class CalcButtonListener implements ActionListener{
private boolean run = true;
private java.util.Timer timer = new java.util.Timer();
private JButton start_loop, end_loop;
//here the buttons initialization
#Override
public void actionPerformed(ActionEvent ae){
if(ae.getSource()==start_loop){
java.util.TimerTask task = new java.util.TimerTask() {
#Override
public void run() {
doStuff();
}
};
timer.schedule(task, java.util.Calendar.getInstance().getTime(), 500);//here the '500' means the time, 500 ms,
the task is repeatedly executed.
}
if(ae.getSource()==end_loop){
timer.cancel();//cancel the tasks scheduled
System.out.println("Task cancelled!");
}
}
private void doStuff(){
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
robot.delay(num.nextInt(max-min+1)+min);
}
}
Now, the task doStuff() is scheduled to be executed each 500 ms.
Other info about java.util.Timer and java.util.TimerTask.
About your problem:
The problem is I have another button to set the variable run to false
but once the first button is clicked, I can't click the second button
or the exit button at the top for that matter.
As in a previous question, and in this page, it's written this:
Swing's single-thread rule says that Swing components can only be
accessed by a single thread. This rule applies to both gets and sets,
and the single thread is known as the event-dispatch thread.
The single-thread rule is a good match for UI components because they
tend to be used in a single-threaded way anyway, with most actions
being initiated by the user. Furthermore, building thread safe
components is difficult and tedious: it's a good thing not to be doing
if it can be avoided. But for all its benefits, the single-thread rule
has far-reaching implications.
Swing components will generally not comply with the single-thread rule
unless all their events are sent and received on the event-dispatch
thread. For example, property-change events should be sent on the
event-dispatch thread, and model-change events should be received on
the event-dispatch thread.
For model-based components such as JTable and JTree, the single-thread
rule implies that the model itself can only be accessed by the
event-dispatch thread. For this reason, the model's methods must
execute quickly and should never block, or the entire user interface
will be unresponsive.
Then, if you develop your GUI using a single Thread, when a button event is executed, your GUI will freeze, waiting for the complete execution of the related button event. In your case, on a infinite loop, your GUI will always freezing.
My suggestion is to use, for your GUI, a SwingWorker, or extend the Thread class (then developing the GUI in a separate thread), or implement the Runnable interface. Another alternative is the using of a Timer from the javax.swing.Timer package.
You can read this old question of SO about SwingWorker: How do I use SwingWorker in Java?
A tutorial for SwingWorker : http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
A tutorial to make a Thread : http://docs.oracle.com/javase/tutorial/essential/concurrency/
A question about Timer: Update JPanel and attributes in a gui, with a user-specified timer?
A tutorial about Timer: http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
You should read about Swing timers:
http://java.sun.com/products/jfc/tsc/articles/timer/
That is, make your program event-driven. Swing applications already have a loop running inside them all the time, called the event loop. It doesn't help if you start another one.
Be careful about method like while(something), this could make the program frezee, i recommend you to implement events listeners to avoid this problems...
run=true;
while(run == true){
...
while (run = true) is almost certainly not what you want. What that does is assigns true to run each time the loop condition is executed, which ensures that the loop will always continue.
What you were probably trying to say was while (run == true) which only tests whether run is true. Better is just to say while (run), which does the same thing.
If you're assigning to run from a different thread, note that you ought to make run a volatile member of your class. If you're not assigning to run somewhere else, then you have a logic bug, since there's no way to break out of the loop. In that case, you need to add a test inside the loop and set run to false when you want the loop to stop. (Or, you could have while (true) and just use a break inside the loop.)
Related
I'm trying to make "Realtime Feeling JTable". I made GUI with JTable, and then there is a button named START. When I click this button, the program will call below start() method. (It's just an example). Adding row is no problem, It works well. But the problem is, rows come to Jtable just at one time. (maybe program finish start() method, The rows come up with at all together.) There is 1000 more statement in my start() method. It's not realtime feeling. I shoud wait until start method finished. and then 1000 rows added at once.
void start(){
int a = 3+3;
addRow(new String[]{"a is 6"})
int b = 4+4;
addRow(new String[]{"b is 8 "})
}
What I'm gonna do is just "Real Time Feeling", I want to see adding row one by one, not at once. Could you understand my purpose? Imagine "Fiddler" Program. you can understand easily. Can anyone help me? Should I make a Thread?
Thanks for reading, and sorry for ackward expression(english is not my language)
This is because of the way Swing threading works. There is one single thread that handles all rendering, updating, etc. Your processing of the start() method is happening inside that render thread so nothing else on the GUI will respond until you return from the method.
What you need to do is use a SwingWorker or a ThreadPoolExecutor and farm the processing off to another thread. That can then update the GUI on a regular basis as the processing continues.
http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
Note that by doing this people can potentially click the button multiple times and call start() again before the first one has finished. You will need to consider and handle that case - for example by disabling the button until the first one completes.
Use a Swing based Timer. In the actionPerformed(..) add a new record. See How to Use Swing Timers for details.
In swing there is a thread called EDT (Event Dispatcher Thread) that is responsible for GUI to behave like a real time, user friednly app. If you interfer EDT with a long running method you will freeze your app GUI. You have to do long running tasks in background (in a thread different than EDT). You can do this by using javax.swing.SwingWorker<T,V> or javax.swing.Timer
So I have this one button in a class that extends JPanel:
startTSP.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
scatterPlotPanel.findShortestPath();
}
});
And my problem is, that the method inside the actionListener (by the name findShortestPath() ), is one that has to constantly call repaint() on another JPanel. The problem is, that once I click the button startTSP, it doesn't repaint or anything. It just hangs until the method completes running (which can take a very long time given the input). The only time it repaints is right at the end. If it matters, I can post the method for findShortestPath() but I don't think it matters whats in it, because my question is, how do I get it to not hang and just carry out the method normally as if the method was called in the main class?
I'm sorry if the question is too vague, I will try my best to explain it if anyone doesn't understand (just mention so in the comments).
Also, basically what this method is doing is, permuting through an arraylist of coordinate points recursively, and finding the shortest path between the list of points. It is a simple brute force TSP solution.
If you have a long method executed in the event dispatch thread, it blocks the event dispatch thread, and thus prevents it from doing its job: dispatching other events, and painting the UI. This effectively freezes the GUI until the long method returns. So you must not have long, blocking methods in the event dispatch thread.
Use a SwingWorker to execute your long-running method in a separate thread. Or if what you want is simply a repetitive event to happen every N milliseconds on the EDT, then use a javax.swing.Timer.
You can try using another thread for painting on the other panel. This way you will have two independent threads doing different jobs.
You can use signals for communicating between these threads.
For those of you need to know, I'm writing a plugin for CraftBukkit, which is a modded version of Minecraft, and I'm a beginner level java programmer. I have what I think is a really basic question.
I'm trying to implement a countdown that executes methods to send messages to players every second for 20 seconds.
Obviously, I can't simply create a loop that loops for 20 seconds, because that loop will freeze the main thread until it finishes, which is unacceptable. (There is a lot of game code executing)
What are some approaches to creating a loop that will run passively or without halting the main thread?
The only thing I can possibly think is creating a new thread to run the countdown in.
Surely there is a simpler way to do this?
So you aren't confused, the countdown isn't initialized as part of some main loop, its initialized linearly by a user command listener, but its code executes in the main loop. What I mean by that is I need to actually START a loop that checks time, because this code is only executed once.
Sorry if I'm not being clear or making sense.
I would recommend java.util.Timer if you are not using Swing for GUI/Graphics (not familiar with CraftBukkit, so that will be up to you to determine). In particular, look at forms of schedule which allow a task to be repeated at fixed intervals.
javax.swing.Timer is similar. The biggest difference (aside from the interfaces used to respond to timers being triggered) is that javax.swing.Timer utilizes the EDT (event dispatch thread) to run events. If you're using a Swing GUI, this thread is already present and running and you want to use it. If you're not using Swing or AWT, then this is extra overhead that you don't need.
You would use a java.util.concurrent.Executors.newSingleThreadScheduledExecutor() and conveniently schedule a countdown task at 1-second intervals.
Alternatively, if you task must run on the Event Dispatch Thread (the "GUI thread"), you'll be better served by javax.swing.Timer.
Try javax.swing.Timer:
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
};
new Timer(delay, taskPerformer).start();
I am trying to build a simple tictactoe network game. I need the program to wait until a player makes a move and then continue. In my whileConnected() function at the bottom of my code, I have a while(true) cycle that is supposed to run forever and display a confirmation message when the button is pressed (which is signaled by the fact that the content of the String variable 'message' changes).
The problem is that even if the String message variable changes when the button is clicked, my whileConnected() function never realizes this and the if statement inside the function never evaluates to true. An identical if statement inside the ButtonListener class works fine and displays the desired confirmation message.
How can I solve this problem? I've read and read and I get the idea that I should use Threads (I read about them but I never used them before, that's why it's only a guess). Do I need threads? Can someone explain in short the principle that should be used for this specific problem? (how can I make the program pause until the button is clicked and then continue using relevant information created when the button was clicked). A code example would really lighten up my reading about threads - which is a really abstract topic when one is a beginner.
Below is my code, thanks in advance.
public class Test extends JFrame
{
private Container contentPane;
private JButton btn00;
private static String message = "";
private class ButtonListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
String buttonText = e.getActionCommand();
if (buttonText.equals("My Button"))
{
message = "pressed";
if (message != "")
System.out.println(message+"(executed by ButtonListener)");
}
}
}
public Test()
{
this.contentPane = this.getContentPane();
btn00 = new JButton("My Button");
btn00.setSize(btn00.getPreferredSize());
btn00.setLocation(20,20);
ButtonListener listener = new ButtonListener();
btn00.addActionListener(listener);
// configure frame
this.setSize(300,300);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
// make panel
JPanel panel = new JPanel();
panel.setSize(200,200);
panel.setLocation(10,10);
panel.add(btn00);
this.contentPane.add(panel);
}
public static void main(String[] args)
{
Test gui = new Test();
gui.setVisible(true);
// connected
whileConnected();
}
private static void whileConnected()
{
System.out.println("message is at first empty: "+message);
while (true)
{
// the if below never evaluates to true... why?
if (message != "") // this is never true
System.out.println(message+"(executed by whileConnected)");
}
}
}
If you're using swing, you're already using threads. Swing by its nature has a thread for I/O and a thread for back-end. You do indeed want to use threads here - among other things, putting a thread in wait is a lot cheaper than giving it an infinite loop to churn on.
Listeners are another application of threads, and I wouldn't be surprised if you could get most or all of what you want just by using well-constructed listeners. Alternately, there are these things called semaphores. Semaphores are a way for threads to handle timing - if a thread attempts to lock a semaphore that's already locked, it will wait until another thread unlocks it before continuing (and locking it again). In your case, you might try the following.
Have a button listener, a main function, and a locked semaphore.
Main function starts, does any initial behaviors, and attempts to grab semaphore. since the semaphore is already locked, it holds.
When the button listener fires, one of the things it does is unlock the semaphore.
As soon as the semaphore unlocks, the main function grabs it (thus locking it once more) and does whatever it's supposed to do. Eventually, it finishes that, and attempts to grab the (locked) semaphore again, thus waiting for the next time the button listener fires.
repeat.
Edit: To include and explain the actual accepted solution (from comments below).
Fix: add Thread.sleep(1000); to the inside of the while loop in the whileConnected function.
Explanation: The while loop is an infinite loop that contains nothing but an if statement. This if statement evaluates to false (and therefore does nothing further) for a really long time (at least as far as the computer is concerned). This acts in much the same way an electrical short does - there's nothing to slow it down, so the thread that runs that main function burns up a lot of computing resources doing nothing. This is bad. It is possible that some failsafe kicked in and killed the thread. It is possible that the thread failed or broke something in an uglier manner. In any case, including a sleep statement (in this case, sleeping for a second each loop, as it's measured in ms) prevents this, and thus allows the function to continue indefinitely.
Swing, like most GUI frameworks, is an event driven environment. Basically this means, the application will wait for some kind of event to occur and then will trigger any registered listeners tha are waiting for notification for that event.
In your case, you would simply need to register a AdtionListener to the button. When the user clicks it (or activates via the keyboard), the actionPerformed method will be called and you can execute what ever code you need.
Check out How to write action listeners for more info.
Swing is also a single threaded framwework. That is, all interaction with the UI must be executed from within the context of the Event Dispatching Thread.
This also means, that any long running or blocking task MUST be executed from a separate thread so as not to block the EDT from processing incoming events and repaint request, which could make you application appear as if it has hung.
Check out Concurrency in Swing for more details
To my mind, your game protocol is going to need some way of tracking whose round it is. This would be achieved simply via the use if a virtual token.
The basic concept would be, unless the player has the token, they can't make a move. Once the player makes a move, that move and the token is sent to the other player.
I think by definition the Game is paused until the button is pressed, as the gui will not be running the the main thread, it should be running in the event dispatching thread.
Swing certainly has a number of gotchas around threading and listeners that can result in some unpredictable behaviour when not implemented correctly.
Have you gone through the Java tutorials on oracle for Swing? Most relavent would be the examples in http://docs.oracle.com/javase/tutorial/uiswing/events/index.html for Listeners and http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html for WorkerThreads
I've found with Swing the best thing is to download the examples here and try to expand on them
I agree with Ben Barden though, from my understanding of what you need, I think you can achieve what you need using listeners.
I have a MouseListener Thread where this method is called each time there is a click:
public void mousePressed(MouseEvent event){
//my Logic here
}
Now what happens is that there are so many clicks in little time, while my logic here, takes more time to process. What i was expecting is that further clicks will be discarded and this method will continue on the latest upcoming clicks.
But what is happening that while the logic is processing, the incoming clics are queued and even when clicking is stopped, qued clicks keep calling this mousePressed method, as a result i have multiple delayed executions.
Unfortunatelly i do not have control over the Listener (why it ques and keep sending delayed clicks), so in such a scenario, can you tell me what is the efficient way of handling such that i do not make a que due to delay in my processing.
I think the most obvious way would be using another thread, but i am wondering this might trigger so many threads in little time, or is there any way i can lock just one thread while the rest of clicks just go through empty loop?
Updates: See Phillip's comments as to why this doesn't work for Swing!
It's unfortunate that you don't have access to the eventListener. The ideal solution would be to unregister the callback while it is being executed.
Here's a solution that emulates unregistering the callback function while it is being executed, assuming you want queued clicks to go away:
private AtomicBoolean engaged = new AtomicBoolean(); // thread-safe boolean
public void mousePressed(MouseEvent event){
if (!engaged.get()) {
engaged.set(true);
// your logic here
engaged.set(false);
}
}
The AtomicBoolean acts as a test-and-set lock preventing multiple threads concurrently running the pressed event callback. Queued presses will be dissipated during the lock.
One solution would be to access the component on which the user clicked and disable it during the processing of the MouseListener. This way it doesn't accept new clicks. This would have the additional benefit of giving the user visual feedback that he is not supposed to click on the component at this time.
You can access the component through the MouseEvent:
public void mousePressed(MouseEvent event) {
event.getComponent().setEnabled(false);
try {
// ....
} finally {
event.getComponent().setEnabled(true);
}
In general though you shouldn't do too much computation in the Swing event listener thread, because Swing also needs it for processing other events and painting. You should use separate Threads for doing actual work and just start them in the MouseListener. You can also use an ExecutorService to simplify this.
It would still be a good idea to disable the gui component during the whole computation in order to give the user feedback.
Edit:
This solution of course depends on Swing. Any similar solution depends on the details of your GUI library, so if you use your own GUI library, you are one your own.
You can of course use a general solution involving separate threads for doing the work. I really recommend an ExecutorService here, were you don't need to care about the details and just submit tasks to execute. You can then have a simple volatile boolean variable which indicates whether the action is currently being executed and new clicks should be ignored.