I'm new in java. I'm trying to understand thraeads and timers in java. It was big problem for me to undate ProgressBar from another thread. I read many posts and found answers. So can you say is this the best way to resolw my task. So, what I want is to run second thread and when it's done, stop progress bar and change text on the button. So I creat Frame with button and progress bar in Test class:
After I realize action listener for Button to run second thread:
public class Test extends javax.swing.JFrame {
RunBg thread = new RunBg();
....
// declaration for button, progressbar and main() method
....
private void jButton1MouseClicked(java.awt.event.MouseEvent evt) {
// needed to check is the thread running at first time else creat new object and rund hread again cause thread can be executed once
if(thread == null){
thread = new RunBg();
}
if (jProgressBar1.isIndeterminate()) {
jProgressBar1.setIndeterminate(false);
jButton1.setText("Run!");
thread.suspend();
} else {
jButton1.setText("Working...");
jProgressBar1.setIndeterminate(true);
// let us know is the second thread running or nope so we can know resume it or pause
if (thread.isAlive()) {
thread.resume();
} else {
thread.start();
}
timer.start();
}
}
/*
Then I creat timer to know is my thread still alive else change Progress bar and button
*/
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(!thread.isAlive()){
thread.stop();
jProgressBar1.setIndeterminate(false);
jButton1.setText("Запустить!");
timer.stop();
// thread.interrupt();
thread = null;
}
}
});
...
// end of class
}
And my second class for another thread named RunBG:
public class RunBg extends Thread{
int i = 0;
public void run(){
while(i<20000){
System.out.println(">>>"+i);
i++;
}
System.out.println("<<<Finished>>>");
}
}
Everythig works fine , but I have question. Is there a better way to realize my task or maybe I made some mistakes. Also I thik that my code will help another beginners who looking for help to understand how it's work.
Related
I am unclear on how to use wait() and notify() to pause a thread. I read talk about synchronizing, but I'm unsure how to do it in my instance. I have a music player with a progress bar where I want to pause the thread in control of syncing the progress bar with the music. Here is the thread I want to pause:
#FXML private void clickedButton(ActionEvent event){
shuffle.setOnAction(e -> {
artistPane.setText(model.getCurrentSong());
if(firstTime){
//Multithreading with JavaFX. Essentially this other thread will check the slider to make sure its on track.
sliderThread = new Task<Void>() {
#Override
protected Void call() throws Exception {
boolean fxApplicationThread = Platform.isFxApplicationThread();
System.out.println("Is call on FXApplicationThread: " + fxApplicationThread);
//this is an infinite loop because now I only need to make this thread once, pausing and starting it, as opposed to making many threads
for(;;){
Thread.sleep(100);
progressBar.setValue(controller.getPercentageDone());
}
}
};
new Thread(sliderThread).start();
firstTime = false;
}else if(!model.getIsPlaying()){
//I want to start the thread here
}
controller.shuffle(); //this will start the music on the next song
});
Here is the second half where I also want to pause and start the thread:
play.setOnAction(e -> {
controller.play(); //this will pause/start the music
if(!model.getIsPlaying()){
//where I want to pause the thread.
}else{
//I want to start the thread here
}
});
I will try to give you simple example and you try to apply it to your program...
public class TestClass extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private Thread playThread ;
TestClass() {
playThread = new Thread(new Runnable() {
#Override
public void run() {
System.out.println("DO SOME THING HERE");
System.out.println("SONG WILL PLAY.....");
}
});
}
public void startMyPlayer() {
System.out.println("PLAYING NOW...");
playThread.start();
}
public void pauseMyPlayer() throws InterruptedException {
System.out.println("PAUSED NOW...");
playThread.wait();
}
public void resumeMyPlayer() {
System.out.println("RESUMING NOW...");
playThread.notify();
}
}
That's it.I hope this help you.
I'm making simple game, here is the code:
public class Game extends Canvas implements Runnable {
public void start() {
t = new Thread(this);
t.start();
}
#Override
public void run() {
setVisible(true); // visibility of the thread turn on
while (!t.isInterrupted()) {
if(condition for end the game) {
t.interrupt(); //here i need to destroy the thread
setVisible(false); //visibility to off
}
update();
render();
try {
Thread.sleep(20);
} catch(InterruptedException e) {}
}
}
}
I have another class which extends JFrame and this class is introducing main menu, if my "condition for end the game" is true, the thread dissapears and the menu is visible again, its good, but if i want to start new game again, the behavior of the thread is strange- its seems like the Thread.sleep() method changed from 20 to 10 because all its going faster, probably i need to kill the thread, but i dont know how, thanks
Simple, break the loop:
if(condition for end the game) {
t.interrupt(); //here i need to destroy the thread
setVisible(false); //visibility to off
break;
}
You end the loop and the thread will end.
The easiest way to terminate a Thread is to exit the run function. There is no special handling required, a simple return does the trick.
For you game you might want to consider using a ScheduledExecutorService, which allows you to schedule a Runnable to run at a fixed rate:
executor.scheduleAtFixedRate(gameLoop, 0, 1000/TARGET_FPS, TimeUnit.MILLISECONDS);
Keep in mind that you then need to take out the actual looping of your gameLoop, because that is done by the fixed-rate calling, which will reduce it to:
public void run() {
if (pause == false) {
update();
render();
}
}
Where pause is a boolean should you for some reason want to put the rendering on pause for a while.
With this setup you can terminate the game simply by calling executor.shutdown() which will then suppress any further calls to the runnable.
Not really on-topic, but I'm making a game too, and for pacing I'm using Timer (from swingx):
public class MainGameLoop implements ActionListener{
Timer timer;
public static void main(...){
timer = new Timer(10, this);
timer.start();
}
public void actionPerformed(ActionEvent e) {
...
}
}
Working well to me.
I created JFrame which contains and InternalFrame which draws figures which are moving(each figure is another Thread) I wanted to pauseButtonmake it pause, so I wanted to synchronize them on the passed object.
But when I hit the pause button the whole window is freezing and I cannot hit the play button
Another thing is that only one is running at the time, I want them all running then all pause.
class A extends JFrame{
....
Object o = new Object();
JButtton pauseButton = new JButton("pause");
JButtton playButton = new JButton("play");
B b = new B(o);
pauseButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
synchronized (synchronizator) {
try {
synchronizator.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
playButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
synchronized (synchronizator) {
synchronizator.notifyAll();
}
}
...
}
class B extends JInternalFrame{
Object o;
B(Object o){this.o = o}
./...
many... C thread = new C(o);
....
}
class C extends Thread{
Object o;
booolean running;
public void run(){
while(running){
synchronized(o){
}
}
}
}
Noooo!! ;)
All Swing activity should be done on the AWT Event Dispatch Thread (EDT). Use normal thread-agnostic objects and perhaps javax.swing.Timer (not java.util!) for timing.
You may want to do other things not involving Swing on different threads, but I suggest keeping a really clean separation. That is to say very few objects should be dealing with thread issues.
If you are using the bare low-level Java synchronisation facilities, set a condition prior to notify/notifyAll and put your waits within while loops.
In Java, let's say I have a GUI with 2 buttons, Go and Pause.
When I press Go, "Hello" gets printed out over and over again. When I press Pause, "Hello" no longer gets printed to the screen.
Example: User presses Go button. "Hello" gets printed out for 1 minute until the user presses "Pause."
What is the proper way to express this approach in Java? Is it equivalent to my commented pseudocode within the goButton source?
public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
// while user has not pressed the pause button
printHello();
}
else if(e.getSource() == pauseButton)
{
pause();
}
}
Thanks
In order to get this to work, in reasonable fashion, you will need a Thread. This is executed in the background until such time as you decide to cancel/pause it.
This is an EXTREMELY basic example. Normally I'd wrap the task and the GUI up in appropriate classes rather then accessing static references, but it gives a basic idea
public class TestHello {
private static HelloTask task;
public static void main(String[] args) {
Thread thread = new Thread((task = new HelloTask()));
thread.setDaemon(true);
thread.start();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
JButton goButton = new JButton("Go");
JButton stopButton = new JButton("Stop");
goButton.setActionCommand("Go");
stopButton.setActionCommand("Stop");
ActionHandler handler = new ActionHandler();
goButton.addActionListener(handler);
stopButton.addActionListener(handler);
frame.add(goButton);
frame.add(stopButton);
frame.setVisible(true);
}
public static class ActionHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Go")) {
task.start();
} else if (e.getActionCommand().equals("Stop")) {
task.pause();
}
}
}
public static class HelloTask implements Runnable {
private static final Object WAIT_LOCK = new Object();
private boolean dump = false;
public void start() {
synchronized (WAIT_LOCK) {
dump = true;
WAIT_LOCK.notify();
}
}
public void pause() {
synchronized (WAIT_LOCK) {
dump = false;
WAIT_LOCK.notify();
}
}
#Override
public void run() {
while (true) {
while (dump) {
System.out.println("Hello");
}
try {
synchronized (WAIT_LOCK) {
WAIT_LOCK.wait();
}
} catch (Exception e) {
}
}
}
}
}
Some further read:
Java Concurrency
Concurrency in Swing
Caveats
NEVER try and modify the GUI from any thread other then the Event Dispatching Thread.
To have responsive UI you would usually have to run printHello() in separate thread. Then as you do processing in this thread, for example, after every print statement, you check some flag boolean isPaused; and stop execution if it is true. When pause button is clicked you set the value of this flag to true.
You need to implement your loop in a separate thread. Otherwise the GUI will become irresponsive and the user might not be able to click the Pause button at all.
With this threaded approach, you also need a flag which indicates whether or not to print out the message. The printing loop can simply stop executing the thread when the flag is set to no longer print.
what about htis:
boolean flag=true;
public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
while(true)
{
printHello();
}
}
else if(e.getSource() == pauseButton)
{
pause();
}
}
You can do this in a few ways the simplest being:
You have a boolean flag, keepPrinting and you set it to true when you push the Go button, false when you push the Pause. Next you have a thread somewhere executing a while loop which will print nothing when keepPrinting is false.
The threading here is really important, without it you're going to have your GUI freeze once the user pushes a button as the program prints hello and happily ignores anything else.
Pseudo Code
//GUI
public ThreadedPrinter greeter;
void ButtonGoPushed(args){
greeter.keepPrinting = true;
}
void ButtonPausePushed(args){
greeter.keepPrinting = false;
}
//ThreadedPrinter
boolean keepPrinting
void run(){
while(true){
if(keepPrinting){
print("Hello");
}
sleep(5); //Make sure that this thread yields if the system doesn't do it automatically
}
The good news about java concurrency versus say C++ is that this will just work, you don't have to worry about the boolean being crazy and inconsistent because in java variable sets are atomic. If you want to do more than just set the variable, make a synchronized method that sets the variable and does anything else you want.
Basically to keep UI responsive such task need to be performed in other thread.
There can be various ways in which you can implement this mechanism in java.
I have used simple mechanism of Runnalbe and volatile flag which ensure that thread exists when you call cancelPrint() method
public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
//start the thread here
}
else if(e.getSource() == pauseButton)
{
//call cancel here
}
}
public class HelloPrinter implements Runnable {
volatile boolean cancel = false;
#Override
public void run() {
while (!cancel) {
printHello();
}
}
public void cancelPrint() {
cancel = true;
}
}
I assume you want to do more than just printouts. Take a look at Swing Worker.
It allows you to pretty easily write your GUI-related code that gets executed in the AWT Event Thread and your long-executing code in other thread(s) and pass values back and forth. This will help prevent any GUI lockup issues you might experience.
private class MultipleGensListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
for(int i = 0; i < 25; i++)
{
game.runSimulationOneGen();
changeGrid();
}
}
}
//this is the loop. The changeGrid method displays a game grid on a GUI but
// only the 25th iteration is visible on screen. I would like each one to be
// visible for about a half a second before the loop continues.
// I have seen some questions answered on here that are very close to what I'm asking,
// but I just don't really understand how to apply it to my program..
// thanks for any help.
If the code performed by the simulation is quick and does not consume too much CPU and time, then consider using a Swing Timer to do your looping and delay. Otherwise, you'll need to use a background thread such as can be done with a SwingWorker object.
For e.g. if using both Swing Timer and SwingWorker:
private class MultipleGensListener implements ActionListener {
protected static final int MAX_INDEX = 25;
public void actionPerformed(ActionEvent e) {
int timerDelay = 500; // ms delay
new Timer(timerDelay, new ActionListener() {
int index = 0;
public void actionPerformed(ActionEvent e) {
if (index < MAX_INDEX) { // loop only MAX_INDEX times
index++;
// create the SwingWorker and execute it
new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
game.runSimulationOneGen(); // this is done in background thread.
return null;
}
#Override
protected void done() {
changeGrid(); // this is called on EDT after background thread done.
}
}.execute(); // execute the SwingWorker
} else {
((Timer) e.getSource()).stop(); // stop the timer
}
}
}).start(); // start the Swing timer
}
}
NEVER BLOCK THE GUI EVENT THREAD
you can use a timer for that and have it only fire 25 times
final Timer t = new Timer(500,null);
t.addActionListener(new ActionListener(){
int i=0;
public void actionPerformed(ActionEvent e){
game.runSimulationOneGen();//run 1 iteration per tick
changeGrid();
if(i>25){t.stop();}
i++;
}
});
t.setRepeats(true);
t.start();
btw the reason only the last iteration is shown is that gui updates (redraws) are done in a separate event, but to let another event trigger you need to return from the listener method which you didn't
the Timer I showed is a more elaborate iteration which lets other events run in between iterations allowing the gui to show the changes
check my post that shows both methods java.swing.Timer#setDelay(int)
and
correct usage of Thread.sleep(int)
java wait cursor display problem