My project run on java swing there are 2 button (start/stop) for counting.
When I click start button. There is one thread running (Thread-0) ,then click stop button "Thread-0" disappear but when I click start button many times. there are many threads such as Thread-5, Thread-6, .. Thread-10 are running.
Problem :
If click start and then stop counting is ok. but click start many times it's incorrect counting.
start button
private void btnStartActionPerformed(java.awt.event.ActionEvent evt) {
start();
btnStart.setEnabled(false);
btnStop.setEnabled(true);
}
stop button
private void btnStopActionPerformed(java.awt.event.ActionEvent evt) {
isEnable = false;
btnStop.setEnabled(false);
btnStart.setEnabled(true);
}
start() method :
isEnable = true;
Thread refreshPlan = new Thread() {
#Override
public void run() {
while(isEnable) {
try {
sleep(CYCLE_TIME * 1000);
PLAN += 1;
planValue.setText(String.valueOf(PLAN));
} catch (InterruptedException ex) {
//ignore
}
}
};
};
refreshPlan.start();
Can I run only single thread when click many times in start button ?
any suggestion? thanks.
sorry for my bad english.
There are four significant problems here:
Unless isEnable is declared as a volatile variable, there's no guarantee that a write from one thread will be seen in another thread
Likewise your access to the PLAN counter (which is badly named - please follow Java naming conventions) is unsafe. You may wish to consider using AtomicInteger for this.
You're making changes to the UI from your extra thread. You can't do that - in Swing (and most UIs) all access to UI components has to be done on the thread responsible for that UI. See the Swing concurrency tutorial for more details.
Because you only check isEnabled one per second, it would be possible to stop and start multiple threads in the meantime... leading to several threads being active at once. This could interfere with your counting.
You might find it's better to use a javax.swing.Timer which fires once a second, and just checks whether or not it's meant to do anything. That way everything can be on the UI thread.
start method is instantiating new Thread() that's why every time u click it, it brings new thread into life.
declare Thread refreshPlan class variable then in start method put all the code in this check
if(refreshPlan == null || !refreshPlan.isAlive()){
//ur existing code to instantiate new thread.
}
Related
I have thread which contains a loop
while(isRunning){
}
isRunning is a Boolean variable with the value true, when some clicks on a button it gets false and so it leaves the loop and the run() function of the thread.
I want to create another button that on click it will reenter the run() function.
I am not sure if when I leave the run() function the thread dies or just stops.
I have tried using thread.run() but it didnt work.
Also I have looked for an answer in other's people questins about this matter but nothing seemed to help me. Thanks for the help
When a thread is finish processing it's code, There's no way of restarting it. You can either:
Create a new thread and pass the Runnable to that thread.
If you need to use that run() method often, use an Executor. You can use Executors.newSingleThreadExecutor(), which will supply you with a worker thread. (Reusable thread).
class Example {
static ExecutorService executor = Executors.newSingleThreadExecutor();
static Runnable run = new Runnable() {
public void run() {
}
};
public static void main(String[] args) {
//anytime you wanna run that code..
executor.execute(run);
}
}
If your thread runs to its end, it stops.
It will remain there for you to collect its return status until the thread is cleaned up.
To restart within the same thread, you need an extra control flow.
For instance:
while (restarted) {
while (isRunning) {
}
// Wait for a restart or end click
}
That is what so called worker threads in a thread pool do, which are intended for maximum performance.
But logically, you will probably simply want to create a new thread object and start that one.
new Thread(p).start();
Please read through java concurrency tutorial.
http://docs.oracle.com/javase/tutorial/essential/concurrency/
Just Maybe, guarded blocks might be useful for your case but your case is a little vague to recommend anything specific.
http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
I im creating a simple testing app that runs a check every hour on the selected directory/s using thread.sleep() through JFileChooser. But when i select the directory and the method runs the ui panel goes grey and the swing bits disappear. The thread seems to be putting the ui to sleep as well as the method its calling.
if (option == JFileChooser.APPROVE_OPTION) {
selectedDirectory = chooser.getSelectedFiles();
try {
while (true) {
runCheck(selectedDirectory);
Thread.sleep(1000*5);//1000 is 1 second
}
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
e1.printStackTrace();
}
}
Im looking for a way around this issue so that i can print the results of the checks being run in the ui .setText(result)
You are correct about the code putting the UI to sleep. Since sleep is called on the Event Dispatch Thread (the thread responsible for running the gui) the UI stops processing events and 'goes to sleep'.
I think what you want is a javax.swing.Timer.
Timer t = new Timer(1000 * 5, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do your reoccuring task
}
});
This will cause your reoccurring task to be performed off of the EDT, and thus it wont leave your ui unresponsive.
If the code you have posted runs on the EventDispatchThread, then there is no way Swing can redraw the GUI. You're blocking (sleeping in) the thread that's supposed to handle that!
This is because you are running you check in the main GUI thread and are using an infinite loop. This check is a background task and should be executed in it's own thread so that the GUI can still receive and react to input by the user.
You also do not need to write your own implementation, Java has a Timer object.
Edit: There is also a Swing specific Timer object. This will have the action occur in the GUI thread, so if your task is long, it can cause the GUI to still lock up while the action is occurring (but not while it is waiting).
I im creating a simple testing app that runs a check every hour on the selected directory/s using thread.sleep() through JFileChooser. But when i select the directory and the method runs the ui panel goes grey and the swing bits disappear. The thread seems to be putting the ui to sleep as well as the method its calling.
if (option == JFileChooser.APPROVE_OPTION) {
selectedDirectory = chooser.getSelectedFiles();
try {
while (true) {
runCheck(selectedDirectory);
Thread.sleep(1000*5);//1000 is 1 second
}
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
e1.printStackTrace();
}
}
Im looking for a way around this issue so that i can print the results of the checks being run in the ui .setText(result)
You are correct about the code putting the UI to sleep. Since sleep is called on the Event Dispatch Thread (the thread responsible for running the gui) the UI stops processing events and 'goes to sleep'.
I think what you want is a javax.swing.Timer.
Timer t = new Timer(1000 * 5, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do your reoccuring task
}
});
This will cause your reoccurring task to be performed off of the EDT, and thus it wont leave your ui unresponsive.
If the code you have posted runs on the EventDispatchThread, then there is no way Swing can redraw the GUI. You're blocking (sleeping in) the thread that's supposed to handle that!
This is because you are running you check in the main GUI thread and are using an infinite loop. This check is a background task and should be executed in it's own thread so that the GUI can still receive and react to input by the user.
You also do not need to write your own implementation, Java has a Timer object.
Edit: There is also a Swing specific Timer object. This will have the action occur in the GUI thread, so if your task is long, it can cause the GUI to still lock up while the action is occurring (but not while it is waiting).
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.)
why in my code, the TextView does not take except the last count.
the result just is: counter= 4
int i =0;
while (i< 5) {
try {
Thread.sleep((i*1000));
mText.setText("counter"+ i);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// mText does not show
mText.setText("counter= "+ i);
i = i +1;
}
You are blocking the UI thread when your app sleeps, so the screen is not redrawn, hence you don't see the text changes until your loop ends.
The UI thread
When an application is launched, the system creates a thread called
"main" for the application. The main thread, also called the UI
thread, is very important because it is in charge of dispatching the
events to the appropriate widgets, including drawing events. It is
also the thread where your application interacts with running
components of the Android UI toolkit.
For instance, if you touch the a button on screen, the UI thread
dispatches the touch event to the widget, which in turn sets its
pressed state and posts an invalidate request to the event queue. The
UI thread dequeues the request and notifies the widget to redraw
itself.
This single-thread model can yield poor performance unless your
application is implemented properly. Specifically, if everything is
happening in a single thread, performing long operations such as
network access or database queries on the UI thread will block the
whole user interface. No event can be dispatched, including drawing
events, while the long operation is underway. From the user's
perspective, the application appears hung. Even worse, if the UI
thread is blocked for more than a few seconds (about 5 seconds
currently) the user is presented with the infamous "application not
responding" (ANR) dialog.
If you want to see how bad this can look, write a simple application
with a button that invokes Thread.sleep(2000) in its OnClickListener.
The button will remain in its pressed state for about 2 seconds before
going back to its normal state. When this happens, it is very easy for
the user to perceive the application as slow.
To summarize, it's vital to the responsiveness of your application's
UI to keep the UI thread unblocked. If you have long operations to
perform, you should make sure to do them in extra threads (background
or worker threads).
More info:
http://developer.android.com/resources/articles/painless-threading.html
That is the problem. And AsyncTask is (one) solution:
AsyncTask
AsyncTask enables proper and easy use of the UI thread. This class
allows to perform background operations and publish results on the UI
thread without having to manipulate threads and/or handlers.
An asynchronous task is defined by a computation that runs on a
background thread and whose result is published on the UI thread.
http://developer.android.com/reference/android/os/AsyncTask.html
First, it would be a good idea to change the text before sleeping.
Second, if you do this in a separate thread, you have, nevertheless, to manipulate a widget in the ui thread. Try this :
instead of
mText.setText("counter"+ i);
try
runOnUIThread( new Runnable() {
public void run() {
mText.setText("counter"+ i);
}
});
Regards,
Stéphane
"while (i <= 5)" will make it go on till five.
Another solution is to make "i" 1 to start with, instead of 0.
Does this help?
Use a for loop (inside a non-UI thread):
for (int i = 0; i < 5; i++}{
try {
Thread.sleep((i*1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUIThread(new Runnable() {
public void run() {
mText.setText("counter"+ i);
}
});
}
Many answers here gave you all the information you need to achieve what you want but it may be difficult to understand.
I will propose you below a new way to achieve it, maybe easier. But I want to underline that you should understand what people wrote, my previuos post and the one from #LAS_VEGAS at least.
Here is something easy : use a handler.
add a data member like
Handler handler = new Handler();
then on click :
for (int i = 0; i < 5; i++}{
handler.postDelayed( new Runnable() {
public void run() {
mText.setText("counter"+ i);
}
}, i * 1000 );
}
I don't have access to my very own computer right now and fear this may not compile due to a non final variable used inside a annonymous inner class. So you could also try to build a named inner class like this in your main java class :
public class ButtonTimerThread implements Runnable
{
privqte int i =0;
public ButtonTimerThread( int i)
{
this.i = i;
}//cons
public void run()
{
mText.setText("counter"+ i);
}//met
}//class
qnd then you click listener method would look like :
for (int i = 0; i < 5; i++}{
handler.postDelayed( new ButtonTimerThread( i ), i * 1000 );
}
As your handler will be created by the UI thread, it will be able to update UI. The handler offers the advantage to have a delay mechanism that doesn't block the UI thread that is reacting to your click action.
I hope this helps,
Stéphane
Generally, good solutions to problems with sleep() involve stopping using sleep(). Blocking the UI thread is always a very bad idea. It makes you application's UI non-responsive.
In your case you want an event to run every second. The correct way to implement that is with a timer.