I have a method called inside a button that run almost an infinite loop. I can't access the other buttons while running this method.
How I make to free the interface to access other buttons while running this method?
//methods inside the button
this.setCrawlingParameters();
webcrawler = MasterCrawler.getInstance();
webcrawler.resumeCrawling(); //<-- the infinite loop method
you need to use a SwingWorker
The way Swing works is that it has one main thread, the Event Dispatch Thread(EDT) that manages the UI. In the Swing documentation, you will see that it is recommended to never to long-running tasks in the EDT, because, since it manages the UI, if you do something computationally heavy your UI will freeze up. This is exactly what you are doing.
So you need to have your button invoke a SwingWorker so the hard stuff is done in another thread. Be careful not to modify UI elements from the SwingWorker; all UI code needs to be executed in the EDT.
If you click the link for SwingWorker, you will see this:
Time-consuming tasks should not be run
on the Event Dispatch Thread.
Otherwise the application becomes
unresponsive. Swing components should
be accessed on the Event Dispatch
Thread only
as well as links to examples on how to use a SwingWorker.
Start a new Thread:
// In your button:
Runnable runner = new Runnable()
{
public void run()
{
setCrawlingParameters(); // I removed the "this", you can replace with a qualified this
webcrawler = MasterCrawler.getInstance();
webcrawler.resumeCrawling(); //<-- the infinite loop method
}
}
new Thread(runner, "A name for your thread").start();
Related
I have a java app in which I use a thread. My thread runs every 20 seconds but, when it runs it kind of blocks the UI ... i. e. whenever the thread is running and the user clicks on button on anything on UI it doesnt respond sometimes.
Could anyone tell me a solution to this problem.
in java Swing, any change made to the state of the UI should be done in a single pre-existing thread called the EDT (event-dispatcher thread).
if that's not the case, you typically experience weird glitches / freezes. one of the most common symptom is that part of the UI becomes gray (except since java 1.6, where the ui is still painted like before, but unresponsive).
the good way to go is to use a dedicated method to queue your changes to the UI in the EDT :
SwingUtilities.invokeLater(Runnable toRunInEDT);
note that if you call invokeLater, the runnable is executed after all currently queued event have been dispatched. that means that the next line of code could be executed before the code in the runnable. there is a synchronous version as well (which should not be executed from the EDT):
SwingUtilities.invokeAndWait(Runnable toRunInEDT);
Some additional tips, on top of what edralzar said:
You can use the convenience method SwingUtilities.isEventDispatchThread() to check if code is in fact running on the event dispatch thread. And, like edralzar said, any code that creates GUI components, modifies the state of GUI components or reads the state of GUI components should run on the Event Dispatch Thread.
Another thing to consider, however, is that code running on the EDT should be able to execute rather quickly. So you cannot just solve things by running everything on the event dispatch thread. If you do the following, your GUI will be frozen for five seconds:
SwingUtilities.invokeLater(new Runnable(){
public void run(){
try{
Thread.currentThread().sleep(5000);
}catch(InterruptedException e){
//Ignored in this example
}
}
});
Why is it frozen? Because all GUI events and GUI updates are performed on the EDT, and if the EDT sleeps for 5000 miliseconds, no GUI updates can be performed during that time.
This article might be an interesting read.
Sounds to me like the thread you're referring to (that runs every 20 seconds) is also the thread that governs the UI.
The solution is to separate the two processes onto different threads.
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.)
Hey.. i have a program that check-in some people.. if they have a dept, the prog will show a panel with a list..
so i wanna to set the panel not visible after a few seconds.. how could i do that?
I created a new Thread (FadeThread) and started it in the view, but now, when i write
Thread.sleep(5000)
the whole program sleeps for this 5seconds,.. i only want to wait in the background for 5seconds and set the panel visible(false) but the whole UI and the program sleeps..
Thanks...
Thread.sleep() will suspend the current thread, which I presume is the event dispatcher thread in your case. If you do it in a background thread, it should work, although if you accidentally call run() instead of start(), it won't.
To execute code with a set delay, you'll have to use the Timer class. Much cleaner and you don't have to worry about accidentally creating too many threads, not exiting a thread properly and so on.
You are causing the Event Dispatch Thread to sleep so the GUI can't react to events.
Read the section from the Swing tutorial on Concurrency for more information and a potential solution.
Assuming you're using Swing, have a look at the SwingWorker class. You can create a process that will block for 5 seconds and then hide the panel in a thread-safe manner.
Alternatively you could used a ScheduledExecutor as long as you make sure you run it on the EDT using invokeLater().
I have a function that takes a while and want to display a waiting screen :
Loading.showSplash("Working...");
for (FileListRow row : model.getList()) {
performAction(row);
}
Loading.hideSplash();
The problem is that the performAction(row); seems to be executed before the loading screen and hence it defeat the purpose.
Any help on solving this to force the waiting screen to show before the rest is executed and to force the function to finish before the waiting screen goes away.
Can I assume this is a Swing program?
If yes, then it looks like you're performing your long-running operations on the event dispatch thread. This is a bad idea; you should perform all long-running operations on a workert hread. See the Java tutorial for more information.
And if you do perform long-running operations on a background thread, your definitely do not want to use a semaphore to suspend the GUI thread until those operations complete -- or even until they start. The GUI thread should be allowed to run freely and dispatch events, or your UI will have "lags," which are annoying to users. As other posters have indicated, your worker thread can use SwingUtilities.invokeLater() to update the UI.
Try giving this a shot. You should call all code that does GUI updates from the Event Dispatch Thread (EDT). Swing is not thread safe.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Loading.showSplash("Working...");
}
});
new Thread(new Runnable(){
public void run(){
for (FileListRow row : model.getList())
{
performAction(row);
}
}
}).start();
Loading.hideSplash();
I'll assume you are developing a Swing application.
Read the javadoc of the SwingWorker class : http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html.
The idea is to
1. display your splash screen
2. start a thread which performs all your actions in the background.
3. when the background operations are finished, hide the splash screen
The SwingWorker takes care of this. Note however that the background operations may not use any Swing-related component or class, since they're not thread-safe and must execute in the event dispatch thread (EDT).
Another, simpler approach would be to display your splash screen, and then use SwingUtilities.invokeLater to perform your operations and hide the splash screen. This way, everything will be done on the EDT, but the splash screen will be displayed before the other operations are performed. Note however that with this technique, the application GUI will be unresponsive until the splash screen is hidden.
I'm developing a small app, which would have Swing GUI. App is doing IO task in another thread, when that thread finishes GUI should be updated acordingly to reflect thread's operation result. Class running in a (worker, non-GUI) has object passed to it in contructor which would be used for updating GUI, so I don't need to put GUI stuff in a non-GUI class, but rather pass object for updating GUI to that class.
As I understand form reading here, (thread/swing) safe options for updating (changing) Swing GUI would be to use javax.swing.SwingUtilities.invokeLater(), javax.swing.SwingUtilities.invokeLaterWait() and/or javax.swing.SwingWorker() which basically are doing the same thing.
This all threading issue with Swing is a little confusing for me, and yet I need to use threads to do anything meaningful in GUI apps and not hung GUI while processing in EDT, so what interests me for now is this:
Are invokeLater and invokeLaterWait like sending message to EDT and waiting for it do it when it finishes processing messages that were before that call?
is it correct from Swing thread safety aspect, to do something like this:
interface IUPDATEGUI {
public void update();
}
// in EDT/where I can access components directly
class UpdateJList implements IUPDATEGUI {
public void update() {
// update JList...
someJList.revalidate();
someJList.repain();
}
}
class FileOperations implements Runnable {
private IUPDATEGUI upObj;
List<File> result = new ArrayList<File>; // upObject is accessing this
public void FileOperations(IUPDATEGUI upObj) {
this.upObj = upObj;
}
private void someIOTask() {
// ...
// IO processing finished, result is in "result"
}
public void run() {
someIOTask();
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
upObj.update(); // access result and update JList
}
}; );
}
}
In case this isn't correct then how should this be done?
If I could, I would prefer to use invokeLater instead of SwingWorker if possible, because I wouldn't need to change my whole class and it's somehow more neat/distinct me (like sending a message in Win32 apps).
Thanks in advance.
Using invokeLater() and invokeAndWait() passes the Runnable parameter into the queue awaiting execution in the EDT. So calling invokeLater() will cause the Runnable to execute in the EDT when the EDT is able to process the request. invokeAndWait() simply waits (in the calling thread) until this execution takes place.
Using SwingWorker is ideal if you want to do background tasks that notify the EDT either at the end of execution or in intermediate states. An example would be to pass the current progress of a process to a JProgressBar.
For your example it seems that SwingWorker is a better choice but if you don't want to change your code too much then calling invokeLater() when the process is done will be just fine.
I'd recommend not using the invokeAndWait until java 7. I found a spurious wake-up on this method that can cause really painful bugs. For me it led to some really rare and hard to debug null pointer exceptions.
http://bugs.sun.com/view_bug.do?bug_id=6852111
It's fixed as of java 7 b77.
invokeLater is fine. This puts the call into the AWT event queue, so that it will get executed in the EDT in due course. Your program will continue running, and does not wait for your callable to get called.