I have a program that rolls dice, and uses a new thread to loop through in order to update the image and repaint. Here is my code:
public int roll()
{
new Thread(
new Runnable() {
public void run() {
synchronized(o) {
o.notify();
for (int i = 0; i < 10; i++) {
image = randomImage();
repaint();
try {
Thread.sleep(100);
}
catch(InterruptedException ex) {
System.out.println("InterruptedException caught");
}
}
}
}
}
).start();
synchronized(o) {
try {
o.wait();
}
catch(InterruptedException ex) {
System.out.println("InterruptedException caught");
}
}
return rolled;
}
In my other class, I have:
int rolled = dicePanel.roll();
label.setText("Rolled a + rolled");
The problem is that with the current code with synchronization, the dice images do not animate, but do return the correct int rolled. Without the synchronized code, the images will animate but the roll method will return a 0 as the int because it does not let the other thread finish.
Is there any way to have the image code loop through and repaint each time, but wait until the thread has finished to return the int rolled?
This looks like an overly complicated solution. You should certainly perform your dice rolling / image updating in thread other than the EDT, but you needn't split that task into two separate threads.
Just have one thread that fiddles with your dice images then when it's finished doing that, it can set the chosen dice value in your label (and presumably in your image too).
Put the o.notify(); to the end of the run() method. Btw. using notifyAll() should be preferred. Or you may find useful the Future object pattern. Here is an article about it http://www.vogella.com/articles/JavaConcurrency/article.html
Or if you are developing Swing application look to SwingWorker. However, SwingWorker is probably overkill for this task.
Wait¬ify is quite low level api and there are many good abstraction in Java to work with concurrency.
Related
Hi everyone i would like to ask if is it possible not to use timer in Java netbeans to display on my JLabel all value of my variable "counter" using while loop. Here is my sample code.
int counter = 0;
while (counter < 10) {
lblDisplay.setText("Completed " + Integer.toString(counter));
try {
Thread.sleep(1000);
lblDisplay.setText("Completed " + Integer.toString(counter));
} catch (InterruptedException ex) {
Logger.getLogger(Increment.class.getName()).log(Level.SEVERE, null, ex);
}
counter++;
}
in using of system.out.println it was displayed but in my Label was not.
Yes it's possible to avoid using a Swing Timer to achieve this, but if you did this then:
You'd have to make sure that the loop and the Thread.sleep(...) were run in a background thread off of the Swing event thread. If you don't do this, you will freeze the event thread and thus freeze your GUI and render it useless.
And then you'd have to make sure that when you only make Swing calls from the background thread, you take pains to queue those calls onto the Swing event dispatch thread. If you don't do this, you will run the risk of causing occasional very hard to debug threading errors.
Because of the extra work involved and the risk of getting it wrong, you'll find that it is much simpler and safer to just use a Swing Timer. For instance your posted code looks like it's at grave risk of putting the entire GUI/application to sleep since it has both a while loop and a Thread.sleep(...) called without concern for threading.
For example, without a Timer, your code could look something like (caveat: code not compiled nor tested):
new Thread(new Runnable() {
public void run() {
int counter = 0;
while (counter < 10) {
lblDisplay.setText("Completed " + Integer.toString(counter));
try {
Thread.sleep(1000);
final int finalCounter = counter;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
lblDisplay.setText("Completed " + finalCounter);
}
});
} catch (InterruptedException ex) {
Logger.getLogger(Increment.class.getName()).log(Level.SEVERE, null, ex);
}
counter++;
}
}
}).start();
That's a bit more complicated than I like, while the Swing Timer could look like:
int delay = 1000;
new Timer(delay, new ActionListener() {
private int count = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (count < 10) {
lblDisplay.setText("Completed " + counter);
} else {
((Timer) e.getSource()).stop(); // stop the Timer
}
counter++;
}
}).start();
Which is simpler and safer than the previous.
Edit, found a work around, code is posted at the bottom.
Is it possible to pause a thread that's in the middle of using thread.sleep(sleeptime)? And then when it's resumed, sleep for the remaining time?
I tried implementing the wait/notify set up here to pause a thread
How to pause/resume thread in Android?
However, it looks like it waits until thread.sleep is finished with it's sleeping time.
The reason why I am asking this because I am using a while loop inside a thread to play music from a play list, and I need the thread to sleep for the duration of the music track so that the music track finishes before the while loop plays the next song on the playlist.
For example, the while loop will play a song that has a duration of 50 seconds, so the thread.sleep method will pause the thread that contains the while loop. But, the user wants to the pause the song, and the whole play list 25 seconds into the song. So the user will press the pause button 25 seconds into thread.sleep's 50 second sleep. After the user unpauses, I would like the thread.sleep to continue the rest of 50 second wait (25 seconds left at this point) to complete playing the rest of the song. Then the while loop will play the next track on the playlist.
Runnable whilePlay2 = new Runnable() {
#Override
public void run(){
while (k <= myIntArray2.size()-1 ) {
System.gc();
if( myIntArray2.get(k)>count){
video_column_index = videocursor
.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
videocursor.moveToPosition(myIntArray2.get(k)-count);
filename2 = videocursor.getString(video_column_index);
}
else{
music_column_index = musiccursor
.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
musiccursor.moveToPosition(myIntArray2.get(k));
filename2 = musiccursor.getString(music_column_index);
}
try {
if (m2MediaPlayer.isPlaying()) {
m2MediaPlayer.reset();
}
m2MediaPlayer.setDataSource(filename2);
m2MediaPlayer.prepare();
m2MediaPlayer.start();
Thread.sleep(m2MediaPlayer.getDuration());
synchronized(Lock){
while(onS){
Log.i("should be", "waiting");
Lock.wait();
}
}
Log.i("what is on", "on is"+onS);
Log.i("k Check", "k is" +k);
} catch (Exception e) {
}
m2MediaPlayer.stop();
m2MediaPlayer.reset();
k++;
}
} //closes public void run(){
// TODO Auto-generated method stub
};
Thread whilePlay2T = new Thread(whilePlay2);
whilePlay2T.start();
And here is my pause button
public void onToggleClicked(View view) {
// Is the toggle on?
boolean on = ((ToggleButton) view).isChecked();
if (on) {
mMediaPlayer.pause();
length=mMediaPlayer.getCurrentPosition();
m2MediaPlayer.pause();
length2=m2MediaPlayer.getCurrentPosition();
synchronized (Lock){
onS=true;
}
} else {
mMediaPlayer.seekTo(length);
mMediaPlayer.start();
m2MediaPlayer.seekTo(length2);
m2MediaPlayer.start();
synchronized (Lock){
onS=false;
Lock.notifyAll();
}
}
}
EDIT
Looks like I can't use it. So now I am using a recursive function instead of a while loop to set up my play list. Here's what one of them looks like.
public void hiplaylist2(int t2) {
k=t2;
if(k <= myIntArray2.size()-1 ){
System.gc();
if( myIntArray2.get(k)>count){
video_column_index = videocursor
.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
videocursor.moveToPosition(myIntArray2.get(k)-count);
filename2 = videocursor.getString(video_column_index);
}
else{
music_column_index = musiccursor
.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
musiccursor.moveToPosition(myIntArray2.get(k));
filename2 = musiccursor.getString(music_column_index);
}
try {
if (m2MediaPlayer.isPlaying()) {
m2MediaPlayer.reset();
}
m2MediaPlayer.setDataSource(filename2);
m2MediaPlayer.prepare();
m2MediaPlayer.start();
//Thread.sleep(mMediaPlayer.getDuration());
//SystemClock.sleep(mMediaPlayer.getDuration());
Log.i("k Check", "k is" + k);
} catch (Exception e) {
}
}
m2MediaPlayer.setOnCompletionListener(new OnCompletionListener()
{
public void onCompletion(MediaPlayer m2MediaPlayer){
m2MediaPlayer.stop();
m2MediaPlayer.reset();
if(k < myIntArray2.size()-1 ){
k++;
hiplaylist2(k);
}
}
});
};
And then I ues a thread to have more than one of these run at the same time.
I don't really get it. I'm not sure about what you want to do, but i guess that there are at least two problems into your algorithm.
Firstly, when you work on concurrent tasks take care of your race conditions, your m2MediaPlayer seems to be one of them.
Concerning your Thread.sleep(m2MediaPlayer.getDuration()); I believe that you shouldn't do it this way, if you want to wait until your song is finished you just have to :
Launch your song from Thread 1
Pause your Thread 1 (wait)
The song is playing in Thread 2
When the song is finished wake up (notify) your Thread 1 from Thread 2 (and let T2 die)
Note that you have to keep your code as simple as you can, because concurrency is hell.
Is it possible to pause a thread that's in the middle of using thread.sleep(sleeptime)? And then when it's resumed, sleep for the remaining time?
Assuming that you are asking about the behaviour of the Java runtime libraries, then the answer is No.
There is no safe way to pause and resume a thread. The Thread suspend / resume methods for doing this are unsafe and were deprecated in Java 1.1. (Read this if you want an explanation.)
Even if you use the unsafe methods, they do not magically pause a sleep timer. In fact, it is not clear what would happen to a sleeping thread: it is not specified.
Indeed, I would not totally be surprised if you found that a sleeping thread whose sleep time expired while suspended never woke up ...
I'll leave it to others to deal with the rest of your question.
(I can't figure out from your description what it is you are trying to do here ... and why you even need to pause a sleeping thread. On the face of it, it sounds like you should be using wait / notify or a higher level synchronization class ... rather than sleeping. The fact that you apparently tried and failed does not make that the wrong solution.)
so for the game I'm creating I realized I mist have done something wrong with my gameLoop because the first time I play through my game it runs great, but the second time or anything after that, it slows down by about half. Even if I minimize the game (Because that stops the gameLoop, and then bringing it up again starts it back up) here is the gameLoop code:
public void gameLoop(){
new Thread(){
public void run() {
while(gameRunning){
try {
Thread.sleep(1000/60);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (tutorial){
updateBullet();
updatePlayer();
repaint();
} else {
updateEnemies();
updateBullet();
createEnemies();
updateParticles();
updatePlayer();
repaint();
}
}
repaint();
}
}.start();
}
I start it for the first time in the init() just with
gameLoop();
And then I also have:
public void stop(){
bg.stop();
gameRunning = false;
}
public void start(){
bg.start();
gameRunning = true;
gameLoop();
}
And finally the playerUpdate also stops the loop with (The Thread inside of player is to allow for some effects to finish after the player dies):
public void updatePlayer(){
if (player.isMovingLeft){
player.x-=3;
}
if (player.isMovingRight){
player.x+=3;
}
for (int j=0; j < enemies.size(); j++){
if (player.isAlive){
if (enemies.get(j).x+19 > player.x && enemies.get(j).x < player.x+40 && enemies.get(j).y > player.y && enemies.get(j).y < player.y+40) {
enemies.remove(j);
j--;
explode.setFramePosition(0);
explode.start();
for (int k = 0; k <21; k++){
addParticle(player.x+20,player.y+20,2);
}
new Thread(){
public void run() {
try {
Thread.sleep(2000);
gameRunning = false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
player.isAlive = false;
break;
}
}
}
}
And then it is restarted with in the keyPressed event:
if (!gameRunning){
if (arg0.getKeyCode() == KeyEvent.VK_ENTER){
enemies.clear();
bullets.clear();
particles.clear();
score = 0;
player.x = 200;
player.isMovingLeft = false;
player.isMovingRight = false;
player.isAlive = (true);
gameRunning = true;
gameLoop();
}
}
So why is it that whenever the loop is stopped and started again, it runs at half the speed? Thanks!
It looks like you're starting a new thread for each gameloop; this means each time the gameloop runs, you've got another thread for the Java VM to handle. This is extremely inefficient, as eventually you'll have 1000 threads running, causing HUGE lag. Is there any way you could rewrite your code without threading there?
Also, what is this supposed to do?
try {
Thread.sleep(1000/60);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Why would you do 1000/60? A: Why not just use 16? B: It seems like you meant something else here, not sure what though
Also, what is the bg variable your reference in your start() and stop() methods?
It looks like you are trying to stop the thread by using the gameRunning boolean. If this is not volatile, then the game loop may not notice changes from the GUI thread that sets it to false and will run forever. Even if it is volatile, you have a race condition if start is called again before the game thread notices the stop command.
You should instead store a reference the the created thread and interrupt it in your stop method.
Also are all your update methods thread safe with respect to the paint method. updatePlayer doesn't look thread safe, and you don't know when paint will be called so it could happen at the same time as your update method. Even if the paint method doesn't write to the shared data, it could still see inconsistent data due to lack of a memory barrier.
I'd suggest doing all the updates in the GUI thread unless it is very slow and you need the multithreading. Look at using a Timer from Swing to to initiate your update logic.
I am trying to stop a for loop that is initiated by pressing a button. The only problem I have found is that the application is literally non-responsive once the start button is pressed. How would I go about making the stop button? At the moment the only way I can stop the application outside of my IDE is to go into task manager and forcibly delete it.
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
String L = "Hello";
int Num = Integer.parseInt(m1.getText());
int Num2 = Integer.parseInt(m2.getText());
nu = Num;
for (int kk = nu; nu > 0; nu--) {
if (O1.isSelected()) {
for (int num3 = nu; nu > 0; nu--) {
try {
try {
Thread.sleep(Num2 * 1000);
} catch (InterruptedException ex) {}
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_F);
} catch (AWTException e) {
e.printStackTrace();
}
}
}
}
This is the code I have, I have been looking around and I think I need to use the SwingWorker class. I am not sure how to implement it though.
Have you put your loop inside the button action handler?
If so remove it and put it in a thread. Else it will block the EDT and app will become unresponsive. Use buttons only to trigger start and stop.
Without code it is hard to determine, but you are probably performing the loop on AWT thread, hence your UI is blocked and you can not press a button.
You should move your endless loop to another Thread, and then the button will work
You are entering in an infinite loop any how. I would suggest to put your loop in a separate thread and when you want to stop it just interrupt that thread on button click.
Move the code into a dedicated thread and use a variable check to end the loop externally.
The first problem is that you're performing this loop inside the Event Dispatch Thread (EDT). This is a thread which is dedicated to handling the user interface. Since you are looping and sleeping in this thread, it is unavailable to handle any other user interactions. So effectively you are locking yourself out of your own program.
The proper way to shut this down is to create a shutdown() method on the loop which will modify a variable that is checked in each iteration of the loop. Then, when you want to shut it down, you just call this method on the Runnable and the loop shuts down.
public class KeyPressRunnable implements Runnable {
private boolean isRunning = true;
public void shutdown() {
this.isRunning = false;
}
#Override
public void run() {
// put your loop in here, and include this code in the loop:
if (!this.isRunning) break;
}
}
i am cutting a big file into blocks, and want to display the rate of the progress.
when i click startCut Button, here is the code to execute:
FileInputStream in = new FileInputStream(sourceFile);
int blockSize = (int)(getSelectedBlockSize() * 1024);
int totalBlock = Integer.parseInt(txtNumberOfBlock.getText());
byte[] buffer = new byte[blockSize];
int readBytes = in.read(buffer);
int fileIndex = 1;
class PBThread extends Thread
{
#Override
public void run()
{
while(true)
{
pbCompleteness.setValue(value);
//value++; //place A
System.out.println(value);
if (value >= 100)
break;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
value = 0;
PBThread pbThread = new PBThread();
pbThread.start();
while(readBytes != -1)
{
File file = new File(targetFilePath + fileIndex);
FileOutputStream out = new FileOutputStream(file);
out.write(buffer, 0, readBytes);
out.close();
value = (int)(fileIndex / (double)totalBlock * 100);// place B
readBytes = in.read(buffer);
fileIndex++;
}
i change the value of the progressbar outside the run method at place B,the problem is that --the grogressbar only show two state: 0% and 100%.
but, if i take away the code in place B, and change the value of the progressbar inside the run method at place A, the problem will disappear.
i know maybe with SwingWorker it can be fixed easily, but i do want to know why this happen,although i change the value out the run method,when i print it out in the run method,it did changed.
how can i fix that problem while changing value outside the run method?
The crux of the problem is that you're updating the component: pbCompleteness on a thread other than the Event Dispatch Thread. You can remedy this using SwingUtilities.invokeLater from within your run() method; e.g.
AtomicInteger value = new AtomicInteger(0);
while (true) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
pbCompleteness.setValue(value.get());
}
});
// Do some work and update value.
}
This will cause the JProgressBar to be updated (and repainted) on the Event Dispatch thread as your worker thread continues to run. Note that in order to refer to value within the "inner" anonymous Runnable instance I have changed it to be an AtomicInteger. This is also desirable as it makes it thread-safe.
You've got two problems here:
You're doing long-running work in the Swing dispatcher thread, which means you're stopping it from processing events. (Try moving windows around etc - it will fail.)
You're updating the UI from the wrong thread at point A. It sounds like you're getting away with this at the moment, but it's still a bug.
You should use SwingWorker or SwingUtilities to address both of these issues. Basically, you mustn't access the UI from a non-UI thread, and you mustn't do long-running work on a UI thread. See the Swing concurrency tutorial for more information.
i found a very simple way to this prob :D . you can use this code and very simlpe handel this error :).
int progress=0;
Random random = new Random();
while (progress < 100) {
//Sleep for up to one second.
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException ignore) {}
progress+=1;
progressBar.setValue(progress);
Rectangle progressRect = progressBar.getBounds();//important line
progressRect.x = 0;
progressRect.y = 0;
progressBar.paintImmediately(progressRect);//important line
}