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.
Related
On devices running Android 2.2, I want to detect when the user has pressed the screen for a certain length of time. Imagine sending a morse code message, with short taps (dots) and longer presses (dashes). I want to react to short taps as soon as the user lifts her finger, and to longer presses after (say) 500 milliseconds, even if she continues to hold her finger down.
I've looked at both FutureTask and ScheduledExecutorService but these look like overkill for this implementation. Or perhaps I just have cold feet about dealing directly with threads, and seeing all the code that is needed to handle them.
Here's simplified pseudo-code for how I have done this sort of thing in other languages:
public boolean onTouch(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
timer = createObjectToCallback(callbackMethod, 500); // milliseconds
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (timer still exists) {
timer.kill();
// Do the short press thing
} else {
// Do nothing. It already happened when callbackMethod was triggered
}
}
}
public void callbackMethod() {
// Do the long press thing. The timer has already auto-destructed.
}
What simple ways are there of doing this in Java?
== EDIT in response to the answer from #zapl ==
Writing code that works is one thing. Understanding how it works is another.
If I understand correctly, the thread that updates the UI is already running in a loop. Let's imagine a very simple case.
The Main activity creates a black canvas, and contains an onTouch method. When it starts, it calls setOnTouchListener. The Main thread now listens constantly to input from the screen. If the way the user is touching the screen has changed, it calls the onTouch method, with information about the change.
Let's say that the onTouch method draws a green circle around the touch point. This circle is drawn using cycles belonging to the Main thread. When the drawing is finished, the Main thread starts checking for new changes from the screen. If there are no changes, then onTouch is not called again, and the green dot does not move.
When the user lifts her finger, the screen provides changed information to the Main thread, and the code in the onTouch method erases the dot.
Create interface
Has the screen detected a change? No: loop
Has the screen detected a change? No: loop
...
Has the screen detected a change? Yes: draw a green dot; loop
Has the screen detected a change? No: loop.
...
Has the screen detected a change? Yes: new position => redraw the green dot; loop
...
Has the screen detected a change? Yes: not touching => remove dot; loop
Has the screen detected a change? ...
Suppose that I want the dot to turn red if the user's finger does not move for at least 500 ms. No move means no callback to onTouch. So I can set up a Handler, which adds itself to the loop in the Main thread. The Main thread now has two actions in its loop.
Create interface
Has the screen detected a change? No: loop
Has the screen detected a change? No: loop
...
Has the screen detected a change? Yes: a touch; draw a green dot; add Handler; loop
Has the screen detected a change? No;
Is it time for Handler to trigger? No: loop.
...
Has the screen detected a change? No;
Is it time for Handler to trigger? Yes: change dot color to red; remove Handler; loop.
Has the screen detected a change? No: loop.
...
Has the screen detected a change? Yes: not touching => remove dot; loop
Has the screen detected a change? ...
Any code executed by the Handler will block the Main thread until it has completed.
Is that an accurate description of what Handler does?
Using threads when you actually don't need parallelism is indeed way overkill because it adds it's own set of problems. What you need is to schedule code that runs in the future, but on the same thread. Android's Handler can do exactly that. You can either schedule a Runnable or a Message to arrive. There is also the derived CountDownTimer for simpler scheduling of periodic events.
But it's probably not required in this case because there is the GestureDetector.
It comes with Android and can distinguish between several types of single, long and double taps. It also behaves consistent with the rest of the system. You will probably want to use that.
More about that http://developer.android.com/training/gestures/detector.html
If you really want to implement your own or just to see an example of how to use a Handler, have a look at GestureDetector's source . It's full of code like the one you've posted (mHandler.hasMessages, mHandler.removeMessages, mHandler.sendEmptyMessageDelayed).
Note: Handler is not standard Java class because scheduling events in the same thread requires a thread to be message queue based and there is no standard solution for that. It's also UI framework dependent how threadsafe the framework is. Android's tries to throw exceptions if you even try to modify the ui from some background thread. The same aproach should however work with Swing's Event Dispatch Thread (SwingUtilities.invokeLater) for example.
Edit: trying to explain Ui thread & handler:
Any code executed by the Handler will block the Main thread until it has completed.
correct. Android's main thread works very simplified like this:
public BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
public void loop() {
while (true) {
Runnable currentTask = queue.take(); // blocks until something happens
currentTask.run();
// repeat.
}
}
public void enqueue(Runnable runnable) {
queue.put(runnable);
}
public static void main(String[] args) {
startThreadsThatReceiveSystemEvents();
enqueue(new Runnable() {
#Override
public void run() {
Activity activity = createStartActivity();
activity.onCreate();
activity.onResume();
}
});
loop(); // fun fact: an android app will never return from here
// it's process is simply killed by the system
}
The real world equivalent is often found in stacktraces pretty far down:
E/AndroidRuntime(20941): at android.os.Looper.loop(Looper.java:130)
E/AndroidRuntime(20941): at android.app.ActivityThread.main(ActivityThread.jav a:3691)
Everything that Android does in terms of starting / stopping Activites, drawing the screen, .. is the result of something getting enqueued so that the run loop evaluates it at some point. A Handler uses the very same queue. And everything you enqueue will be executed interspersed with all the other things that already happen withing that loop. One thread can't do things parallel to itself so it's all sequential. This is why Handler tasks block other tasks.
Your example with touch events is basically correct, it's just not actively looking at the touchscreen. It is getting notified via it's connection to the system. There is essentially another thread (the Binder threads in case you have ever looked at the list of threads) listening for messages from the system and once they arrive, all this thread needs to do is to enqueue them for the main loop. This can automatically wake the loop up if it was waiting.
The main thread queue is actually not a simple BlockingQueue since it needs to support scheduled events as well. It's an android specific implementation called MessageQueue and is partially implemented in native code. The loop method is in it's own class as well (Looper). And the queue isn't using Runnable directly, it's actually a queue of Messages - which can contain a Runnable (in a hidden field) and the run loop when it finds a Runnable in a Message will just execute it like in above sample code.
Each Handler is bound to one Looper/MessageQueue combination (and therefore to 1 thread). Handler.post/sendMessage does the dirty work of constructing a proper message & enqueueing it. The Message has a link back to your Handler so the loop know's which Handler's handleMessage method to call.
Besides using the already existing main thread loop/queue, you're free to create additional queue based threads and Handlers for those. https://stackoverflow.com/a/13369215/995891 contains a tiny example for that.
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.
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.
I have a JFrame with a CardLayout set as its layout manager. This has two JPanel subclasses in it. One is a panel, WordsLoadingPanel, which displays the text "Loading words..." and has a JProgressBar. The other has to actually load the words. This takes a while (about 10-14 seconds for 100 words; it's a pretty selective algorithm), so I want to assure the user that the program is still working. I made the loading algorithm in the panel fire a property change with firePropertyChange(String, int, int), and the WordsLoadingPanel is catching the change just fine - I know this because I added a listener for this event to perform a println, and it works. However, when I change the println to actually changing the JProgressBar's value, it doesn't do anything. I know I'm changing the value right, because if I set the value before the algorithm starts, it works, and it works on the last iteration of the loop. I'm guessing this is because my algorithm is eating the computing power and won't let JProgressBar update.
So, my question is: How do I make my algorithm wait for Swing (would this be the AWT Dispatching Thread?) to finish updating the progress bar before continuing? I've tried:
Thread.yield in each iteration of the loop
Thread.sleep(1000L) in each iteration of the loop, in a try/catch
putting everything in the loop in a SwingUtilities.invokeLater(Runnable)
putting only the CPU-intensive algorithm in a SwingUtilities.invokeLater(Runnable)
EDIT: To further support my hypothesis of the CPU-eating algorithm (sounds like a children's story…), when I set the JProgressBar to indeterminate, it only starts moving after the algorithm finishes.
Does anyone have any suggestions?
Thanks!
To do expensive operations in background, consider using the SwingWorker class. The documentation has examples on how to use it to do tasks that interact with the user interface in a separate thread, including progress display in JProgressBars.
If you have trouble understanding how the class works, consider:
SwingWorker is a generic class that takes two parameters: T, and V
The doInBackground method returns T and is executed in a separate thread.
Since Swing may only be manipulated in the Event Dispatch Thread, you may not manipulate Swing in doInBackground.
The process method takes a List<V> as a parameter and is called asynchronously on the Event Dispatch Thread.
The publish method takes V... arguments and sends them for processing in the process method.
In conclusion:
T is the type of the result of the computation, if any.
V is the type of the data needed to manipulate the user interface.
Your algorithm should run entirely in doInBackground.
The user interface should be manipulated in the process method.
Your algorithm should use publish to send data to the process method.
OK, I've solved it. For anyone who may have a similar problem, my solution was to change the method which begun the algorithm from executing it synchonously to asynchronously (with new Thread(Runnable).start). So, my code is now
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Thread(new Runnable () {
public void run () {
window.keyboardTrainingPanel.initialize();
}
}).start();
}
});
I hope this can help someone! However, if there is a better way to do this, feel free to notify me.
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.)