I have a background thread which will not start processing until it's start variable becomes true:
class MyBackgroundThread implements Runnable {
// ...
public void run() {
while(true) {
if(!start) continue;
doSomethingWith(myValue);
}
}
}
The start variable is set to true from clicking a button on a JFrame which is of course running on the Event Dispatch Thread. There's also a myValue field in the background thread class, which is set from clicking the button:
startBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
backgroundThreadInstance.setMyValue(100);
backgroundThreadInstance.setStart(true);
// ...
}
});
As you can see, it assigns something to myValue before setting start to true. Does this mean that setting myValue and start as volatile is not required? Since myValue is written to first, it will be leaked to the background thread before start is, thus the background thread will never get the chance to process an uninitialised myValue?
Short answer is yes. Though, in practice, eventually the change to true would likely be seen by your thread, in theory it might never happen.
However, agree with #NamshubWriter that there are better ways to do this than an busy/idle loop. I like his proposal to set the integer and then submit it to an ExecutorService. e.g.
public void actionPerformed(ActionEvent e) {
BackgroundRunnableInstance runnable = new BackgroundRunnableInstance();
runnable.setMyValue(100); // could be in the constructor instead
someExecutorService.submit(runnable);
}
One difference is that if they hit the button multiple times you would have several runnables started. Which may or may not be what you want.
Related
I'm doing too much work on the main thread, and so I want to learn how to run some things on other threads. But I'm having difficulties understanding how threads work.
From what I've gathered, you can no longer stop or cancel a thread, and the way that I need it to work is that, if Button A is clicked, do the function, if Button B is clicked, stop as to not use resources.
Here's where I stand:
> //Inside OnCreate
Thread newthread = new Thread(new Runnable(){
#Override
public void run(){
while(bool) {
...
}
...
}
});
newthread.start();
Button A Listener(){
bool = true;
}
Button A Listener(){
bool = false;
}
when I first start the thread it runs, but after changing bool to false, and back to true it doesn't.
putting return at the end destroys the thread, but calling start again isn't allowed.
I've tried putting 2 while loops, but it still doesn't switch between the loops.
Is there even a reason to use interrupt? and how would it be implemented?
What I need is to create another thread, have it keep doing something until I click button A, then resume when I click on Button B, that's all.
but calling start again isn't allowed.
Not sure why don't you can not call start again, but I think it's good solution. Base on your requirement tries this code put inside run
while (!Thread.currentThread().isInterrupted()) {
try {
if (loop) {
...
}
} catch (InterruptedException e) {
return;
}
}
Why we need to check Thread.currentThread().isInterrupted() because when your Activity destroy you should call newthread.interrupt() to release your Thread because it can be leak memory.
startbtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
isRun = true;
while(isRun)
runProgram();
}
});
stopbtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
isRun = false;
}
});
When I click the startbtn Button, the whole program just stopped and I can't handle anything.
What's wrong?
The problem is that when you set the variable isRun to true the program enters in the while loop without exiting. This happens in the same thread the GUI is running, so it becomes irresponsive and it cannot handle any other event (as the click to Stop the running action by setting isRun to false).
So, the solution is to run the while loop task in another thread, leaving the thread where the GUI is running free to handle more events.
To run the task in another thread I recommend you to use any of the classes in the concurrent framework such as ExecutorService
Going even further, if your purpose is to submit a task that may be canceled, you could even use Future.
Also, bear in mind to use some synchronization mechanism to guarantee both threads see the latest value assigned to isRun.
I'm trying to wait for a piece of code to return true before I proceed with everything else. I have two classes, each has one instance running.
Main where I open the new object I want to wait for to complete
setupWizard setup = new setupWizard();
setup.setVisible(true);
setup.setCallerRef(new java.lang.ref.WeakReference(this));
synchronized(this) {
while (setup.isItComplete() == false) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
ArrayList<String> accounts = Functions.fetchAccounts();
SetupWizard i want to wait for
public setupWizard() {
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
//get handlers
JButton helpBtn = (JButton)getContentPane().getComponent(9);
JButton saveBtn = (JButton)getContentPane().getComponent(8);
JTextField userName = (JTextField)getContentPane().getComponent(3);
JTextField serverField = (JTextField)getContentPane().getComponent(1);
JPasswordField passwordField = (JPasswordField)getContentPane().getComponent(5);
JScrollPane scrolly = (JScrollPane)getContentPane().getComponent(7);
JLabel customQLabel = (JLabel)getContentPane().getComponent(6);
scrolly.setVisible(false);
customQLabel.setVisible(false);
//theb change btn positions and action listeners
setBounds(100, 100, 435, 220);
changeHelpButton(helpBtn);
helpBtn.setEnabled(false);
changeSaveButton(saveBtn, userName, serverField, passwordField);
registerAccount("hi", "hi");
}
public void registerAccount(String pass1, String pass2) {
if (pass1.equals(pass2)) {
Functions.addToDatabase("admin", pass2, 1, 1, 1, 1);
}
setComplete(true);
synchronized(getCallerRef()) {
getCallerRef().notifyAll();
}
dispose();
}
private boolean complete = false;
private Object callerRef;
public boolean isItComplete() {
return this.complete;
}
public void setComplete(boolean variable) {
this.complete = variable;
}
public void setCallerRef(Object mycaller) {
this.callerRef = mycaller;
}
public Object getCallerRef() {
return this.callerRef;
}
As you can see this is only the relevant section of the code. However, what happens is the Main thread freezes, but the second UI that should pop up is just a see-through window with nothing on it. What might be the problem? Or is there any other viable approach to "block code until x becomes true"?
Edit:
Ok I think i figured out a problem with this; if i just initialise the class, and then immediately call registerAccount() from the initialiser method of the second class without trying to do anything with the UI, it works. However, I don't immediately want to go to registerAccount(), because before then there is a semi-long procedure the user has to go through to input all sorts of data, after which the main us has to be unfrozen. Any ideas?
I really can't tell for sure from the code you posted, but I suspect your situation might be that the SetupWizard is a Frame or Dialog and the code in Main is executed by EDT, so when you put EDT on wait in Main you are freezing your GUI and SetupWizard code cannot execute neither.
I suggest you just use a Modal Dialog for SetupWizard. It will block Main while SetupWizard is visible.
You are not using wait() correctly, but it could be very useful in doing what you'd like to do.
First, read the javadoc on Object.wait
Then, in your code where you are currently calling
this.wait()
change this to
synchronized (setup) {
setup.wait();
}
In setup, change setComplete to
public synchronized void setComplete(Boolean variable) {
this.complete = variable;
if (variable) {
this.notifyAll();
}
}
This is the basic framework (there's plenty built on top) of how you do efficient multithreaded locking / waiting / synchronizing
There are two different objects, the notifyAll you invoke is on the WeakReference object where as you wait on the object of Main.
In the registerAccount method of setupWizard class you need to do changes as below so that you notify on the same object as the object upon whose lock Main thread is waiting.
Object lock = ((WeakReference)getCallerRef()).get(); // this will be object of Main in case its not null.
if( lock != null){
synchronized( lock ) {
lock.notifyAll();
}
}
Edit One basic issue in the code you have is that you have justthe single thread. Same thread goes in wait and same thread tries to invoke notifyAll. If this thread goes in wait state then there is no thread which can invoke notifyAll , this results in application being in hanged state forever. You need to invoke the setupWizard constructor from a separate thread.
EDIT 2
there are many issues with your code.
You need to have separate thread calling the registerAccount so that notifying thread is different from the waiting thread.
Your constructor invokes currently the registerAccount which tries to synchronize on the lock objct but you pass the lock object from main after contructor is invoked.
your object on which you wait and on which you invoke notifyall are different objects
I have a little application counting time by pressing a button,
I just use thread.sleep() to count.
When the button is pressed, it triggers the ActionListener which is a class that perform a thread.run(). The thread.sleep() is then started from inside the run() function.
//The Thread
class twentyMins implements Runnable
#Override
public void run() {
try {
Thread.sleep(1000*60*20);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
//The ActionListener
class Reset implements ActionListener {
public static twentyMins count = new twentyMins();
#Override
public void actionPerformed(ActionEvent event) {
count.run();
}
}
The issue is, the button will not bounce up and be able to be pressed again.
And the application can't even be stopped by pressing the EXIT button on the JFrame.
Straightforwardly, I think my application is frozen while the thread.sleep() is running.
Is there something better then Thread.sleep()?
You didn't actually start a background thread here. Any object can implement Runnable (and the run method) but that doesn't make it a thread. Hence when your Reset button is clicked, it locks up the single thread responsible for the UI.
You need to pass your Runnable object to the constructor of the Thread class (java.lang.Thread), as described in the official docs.
What did you expect? You are calling
count.run();
Which will run in same main thread thereby blocking it for 20 mins. Consider creating a thread and calling start() on it.
To perform sleep() on main thread will block the UI.
You could create another thread or just use java.util.Timer class to finish this task.
I have implemented Conway's Game of Life problem in Java swing. Everything is working fine. As you can see in the screenshot below, whenever the "Tick" button is clicked, the game progresses to the next life form. Now, I am planning to include an "Autoplay" button alongside "Tick" button. The purpose of this autoplay is simple. When I hit it, an automated operation should carry on as if I am pressing tick button at an interval of 1 second.
I tried this. But this seems to block all the other operations. How to do this action in a separate thread? A small code snippet would get me going.
class AutoPlayListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == btnAutoPlay){
while(true){
Thread.sleep(1000); //InterruptedException try catch hidden
btnTick.doClick();
}
}
}
}
Use a javax.swing.Timer. It will be able to work with the existing ActionListener if the while(true) and Thread.sleep() calls are removed.
As #Ranman said you're blocking main UI thread. I believe SwingUtilities.invokeLater is usually used for things like this.
There are two options:
Start a new thread. The thread will contain the while loop, and execute a method that processes the array. In each iteration, call repaint() or invalidate() on your window to tell it that it needs redrawing.
Use a Timer. The GUI thread will call your routine at regular intervals.
Threads:
In actionPerformed method, create a new Thread. and call its start method.
The Runnable of the thread should run a while loop (as you have already done), and then simply exit.
Timer:
Create an object in your class of type Timer. Use the one in java.swing.Timer if you are using swing (there is also java.util.Timer which isn't good for GUI ops). The timer should have an ActionListener that calls your method once, but the Timer has a repeat rate of 1000ms.
Tips
to invoke the action, you should put it in a separate method, rather than directly under the button handler. That way, you aren't calling GUI stuff from outside the GUI thread.
e.g.
tickButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
doTick();
}
});
The mechanism to stop the thread is equally important! In general, don't use a while(true) in a thread as it will get lost; invent a semaphore to terminate it.
use a JToggleButton rather than Button?
Synchronization:
If you use threads, you will need something like this, to prevent new threads being created each time the button is pressed:
Code
Thread autoplayThread = null;
Object lock;
boolean autoplaying = false;
public void actionPerformed(ActionEvent e){
synchronized(lock){ // prevent any race condition here
if(!autoplaying && autoplayThread==null ){
autoplaying = true;
autoplayThread = new Thread(new Runnable(){
public void run(){
try{
while(autoplaying){ .... }
}finally{
synchronized(lock) {
autoplaying=false;
autoplayThread=null;
}
}
}
});
autoplayThread.start();
}else{ // stop the thread!
autoplaying=false;
}
}
}