Implementing JComponent blinking in Java using threads - multithreading

I am trying to implement a program where I want different Components to blink at different speeds. I am using threads. But its not working.
How can I implement this.
This is the void run function in the class that implements runnable
public void run()
{
try
{
while(true)
{
Thread.sleep(1000);
if(isVisible()==true)
{
setVisible(false);
}
else
{
setVisible(true);
}
repaint();
}
}
catch(InterruptedException e)
{
}
}
}
and this is the class (its in a paint component of the main JPanel)where I call the threads-
{
cars[i]=new Car(color, xLocation, yLocation, speed, type, i, widthController, heightController);
cars[i].setBounds(widthController+(xLocation*50)+10, heightController+(yLocation*50)+10, 30, 30);
add(cars[i]);
threads[i]=new Thread(cars[i]);
threads[i].start();
}
cars is an array of JComponents of which void run is part of.
Thanks

With Swing, all operations that affect visible components should be run on the AWT-EventQueue. This is a dedicated thread for Input/Output operations as well as drawing and component operations. My recommendation is to use a swing timer for your run operation. The repaint call you made will call the paintCompnent method on the AWT-EventQueue. However you're changing the state of visibility on a seperate thread. This means that by the time the repaint call is made, it's possible the state has already changed to the previous value.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
//Rest of code above...
//This will execute the timer every 500 milliseconds
Timer aTimer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent pE) {
aComponent.setVisible(!aComponent.isVisible());
}
});
aTimer.start();
Another option is that on each thread add this call:
//This should be added inside of your thread
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
aComponent.setVisible(!aComponent.isVisible());
}
});
Here's the answer I was alluding to in my comments:
public void run()
{
try
{
while(true)
{
Thread.sleep(1000);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setVisible(!isVisible());
}
}
});
}
catch(InterruptedException e)
{
}

Related

Updating javafx textArea elment using separated thread or task

I'm trying to update text inside a javafx textArea element instantly to show execution information using both thread and task but nothing seems working, althought when I print something in console it works thus the thread is executing. The program prints all the messages once the program is executed, but i want show the messages as the same time as the program is executing.
Here I have my tsak and thread declarations
#Override
public void initialize(URL url, ResourceBundle rb) {
System.setProperty("webdriver.gecko.driver", "C:\\Users/lyesm/Downloads/geckodriver-v0.26.0-win64/geckodriver.exe");
try {
restoreValues();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
text = new Text(this.getLogs());
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Runnable updater = new Runnable() {
#Override
public void run() {
printMessages();
System.out.println(" working on ... \n");
}
};
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
//Platform.runLater(updater);
}
}
});
thread.setDaemon(true);
thread.start();
service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
Platform.runLater(() -> textArea.appendText(logs));
return null;
}
};
}
};
service.start();
}
I'm calling the service from this method
public void launchTest() {
this.setLogs("\n\n");
service.restart();
this.setLogs(" Test starting ...\n");
service.restart();
//this.setLogs(" Opening the navigator \n");
this.setDriver(new FirefoxDriver());
//this.setLogs(" Reaching http://127.0.0.1:8080/booksManager ... \n");
driver.get("http://127.0.0.1:8080/booksManager");
//this.setLogs(" Setting test data \n");
driver.findElement(By.id("lyes")).click();
driver.findElement(By.name("email")).sendKeys(pseudo.getText());
driver.findElement(By.name("password")).sendKeys(password.getText());
//this.setLogs(" Submitting ... \n");
driver.findElement(By.name("submit")).click();
if(driver.getCurrentUrl().equals("http://127.0.0.1:8080/booksManager/Views/index.jsp") == true) {
//InputStream input= getClass().getResourceAsStream("https://w0.pngwave.com/png/528/278/check-mark-computer-icons-check-tick-s-free-icon-png-clip-art-thumbnail.png");
//Image image = new Image(input);
//ImageView imageView = new ImageView(image);
Label label = new Label(" Test successed");
testsInfos.getChildren().add(label);
}else {
Text textRes = new Text("\n Test failed ");
textRes.setFill(javafx.scene.paint.Color.RED);
testsInfos.getChildren().add(textRes);
}
driver.close();
}
And here the printMessage method called from the thread
public void printMessages() {
String ll = this.getLogs();
this.text.setText(ll);
testsInfos.getChildren().remove(text);
testsInfos.getChildren().add(text);
textArea.clear();
textArea.setText(ll);
}
Neither method seems to work.
Does anybody have any idea how to fix it ?
Edited:
package application;
import java.util.concurrent.CountDownLatch;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
private Service<Void> service;
#Override
public void start(Stage primaryStage) throws InterruptedException {
StackPane root = new StackPane();
TextArea ta = new TextArea();
ta.setDisable(true);
root.getChildren().add(ta);
Scene scene = new Scene(root, 200, 200);
// longrunning operation runs on different thread
/*Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Runnable updater = new Runnable() {
#Override
public void run() {
incrementCount();
}
};
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
// UI update is run on the Application thread
Platform.runLater(updater);
}
}
});
// don't let thread prevent JVM shutdown
thread.setDaemon(true);
thread.start();*/
primaryStage.setScene(scene);
primaryStage.show();
service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(new Runnable() {
#Override
public void run() {
try{
ta.appendText("\n Printed ");
}finally{
latch.countDown();
}
}
});
latch.await();
return null;
}
};
}
};
service.start();
showIT();
}
public static void main(String[] args) {
launch(args);
}
public void showIT() throws InterruptedException {
service.restart();
for(int i = 0;i<1000000;i++) {
System.out.println(i);
}
for(int i = 0;i<1000000;i++) {
System.out.println(i);
}
service.restart();
for(int i = 0;i<1000000;i++) {
System.out.println(i);
}
for(int i = 0;i<1000000;i++) {
System.out.println(i);
}
service.restart();
}
}
The two threading rules in JavaFX are:
Long-running code must not be executed on the FX Application Thread, and
Any code that updates the UI must be executed on the FX Application Thread.
The reason for the first rule is that the FX Application Thread is responsible for rendering the UI (among other things). So if you perform a long-running task on that thread, you prevent the UI from being rendered until your task is complete. This is why you only see the updates once everything is finished: you are running your long-running code on the FX Application Thread, preventing it from re-rendering the text area until everything is complete.
Conversely, the code you do run on a background thread (via the Task.call() method) doesn't do anything that takes a long time to run:
#Override
protected Void call() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(new Runnable() {
#Override
public void run() {
try{
ta.appendText("\n Printed ");
}finally{
latch.countDown();
}
}
});
latch.await();
return null;
}
The only thing you do here is schedule an update on the FX Application thread; the call to Platform.runLater() exits immediately. There's no long-running code at all, so no purpose for the background thread on which this runs. (Technically, the call to latch.await() is a blocking call, but it's redundant anyway, since you simply exit the method after waiting.) With this task implementation, there's no difference between calling service.restart();, and ta.appendText("\n Printed");.
So, your showIT() method should be called on a background thread, and can use Platform.runLater() to append text to the text area. Something like:
import java.util.concurrent.CountDownLatch;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
private Service<Void> service;
#Override
public void start(Stage primaryStage) throws InterruptedException {
StackPane root = new StackPane();
TextArea ta = new TextArea();
ta.setDisable(true);
root.getChildren().add(ta);
Scene scene = new Scene(root, 200, 200);
primaryStage.setScene(scene);
primaryStage.show();
// run showIT() on a background thread:
Thread thread = new Thread(this::showIT);
thread.setDaemon(true);
thread.start();
}
public static void main(String[] args) {
launch(args);
}
public void showIT() {
try {
Platform.runLater(() -> ta.appendText("\nPrinted"));
Thread.sleep(1000);
Platform.runLater(() -> ta.appendText("\nPrinted"));
Thread.sleep(1000);
Platform.runLater(() -> ta.appendText("\nPrinted"));
Thread.sleep(1000);
} catch (InterruptedException exc) {
Thread.currentThread().interrupt();
}
}
}
For your original code, I have to make some guesses about which parts of the API you're using are long-running and which aren't. I would start by creating a utility log() method that you can call from any thread:
private void log(String message) {
Runnable update = () -> ta.appendText(message);
// if we're already on the FX application thread, just run the update:
if (Platform.isFxApplicationThread()) {
update.run();
}
// otherwise schedule it on the FX Application Thread:
else {
Platform.runLater(update);
}
}
And now you can do something like:
public void launchTest() {
log("\n\n");
log(" Test starting ...\n");
log(" Opening the navigator \n");
Task<Boolean> task = new Task<>() {
#Override
protected Boolean call() throws Exception {
this.setDriver(new FirefoxDriver());
log(" Reaching http://127.0.0.1:8080/booksManager ... \n");
driver.findElement(By.name("email")).sendKeys(pseudo.getText());
driver.findElement(By.name("password")).sendKeys(password.getText());
driver.get("http://127.0.0.1:8080/booksManager");
log(" Setting test data \n");
driver.findElement(By.id("lyes")).click();
log(" Submitting ... \n");
driver.findElement(By.name("submit")).click();
boolean result = driver.getCurrentUrl().equals("http://127.0.0.1:8080/booksManager/Views/index.jsp");
driver.close();
return result ;
}
};
task.setOnSucceeded(e -> {
if (task.getValue()) {
//InputStream input= getClass().getResourceAsStream("https://w0.pngwave.com/png/528/278/check-mark-computer-icons-check-tick-s-free-icon-png-clip-art-thumbnail.png");
//Image image = new Image(input);
//ImageView imageView = new ImageView(image);
Label label = new Label(" Test successed");
testsInfos.getChildren().add(label);
} else {
Text textRes = new Text("\n Test failed ");
textRes.setFill(javafx.scene.paint.Color.RED);
testsInfos.getChildren().add(textRes);
}
});
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}

Flutter Platform Channels - Invoke channel method on android, hangs the ui

I'm trying to use Tesseract in flutter using the following package https://github.com/arrrrny/tesseract_ocr
I've download the app and run in.
The problem is that the extractText hangs the UI.
Looking at the Java code:
Thread t = new Thread(new Runnable() {
public void run() {
baseApi.setImage(tempFile);
recognizedText[0] = baseApi.getUTF8Text();
baseApi.end();
}
});
t.start();
try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); }
result.success(recognizedText[0]);
I can see that it is running on a new thread, so I expect it not to hang the app, but it still does.
I found this example:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Call the desired channel message here.
baseApi.setImage(tempFile);
recognizedText[0] = baseApi.getHOCRText(0);
baseApi.end();
result.success(recognizedText[0]);
}
});
from https://flutter.dev/docs/development/platform-integration/platform-channels#channels-and-platform-threading
but it also hangs the UI.
The docs also say
**Channels and Platform Threading**
Invoke all channel methods on the platform’s main thread when writing code on the platform side.
Can someone clarify this sentence?
According to Richard Heap answer, I tried to call a method from native to dart, passing the result:
Dart side:
_channel.setMethodCallHandler((call) {
print(call);
switch (call.method) {
case "extractTextResult":
final String result = call.arguments;
print(result);
}
var t;
return t;
});
Java side:
channel.invokeMethod("extractTextResult","hello");
if I call this method from the main thread, this works fine, but then the thread is blocking.
If I do
Thread t = new Thread(new Runnable() {
public void run() {
channel.invokeMethod("extractTextResult","test1231231");
}
});
t.start();
result.success("tst"); // return immediately
Then the app crashes with the following message:
I also tried:
Thread t = new Thread(new Runnable() {
public void run() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Call the desired channel message here.
baseApi.setImage(tempFile);
recognizedText[0] = baseApi.getHOCRText(0);
baseApi.end();
result.success(recognizedText[0]);
// channel.invokeMethod("extractTextResult", "test1231231");
}
});
}
});
t.start();
result.success("tst");
which is what I understand that Richard Heap last comment meant, but It still hangs the ui.
I had the same Issue and fixed it with a MethodCallWrapper in TesseractOcrPlugin.java
This Code works for me (no Dart-code change is needed):
package io.paratoner.tesseract_ocr;
import com.googlecode.tesseract.android.TessBaseAPI;
import android.os.Handler;
import android.os.Looper;
import android.os.AsyncTask;
import java.io.File;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
/** TesseractOcrPlugin */
public class TesseractOcrPlugin implements MethodCallHandler {
private static final int DEFAULT_PAGE_SEG_MODE = TessBaseAPI.PageSegMode.PSM_SINGLE_BLOCK;
/** Plugin registration. */
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "tesseract_ocr");
channel.setMethodCallHandler(new TesseractOcrPlugin());
}
// MethodChannel.Result wrapper that responds on the platform thread.
private static class MethodResultWrapper implements Result {
private Result methodResult;
private Handler handler;
MethodResultWrapper(Result result) {
methodResult = result;
handler = new Handler(Looper.getMainLooper());
}
#Override
public void success(final Object result) {
handler.post(new Runnable() {
#Override
public void run() {
methodResult.success(result);
}
});
}
#Override
public void error(final String errorCode, final String errorMessage, final Object errorDetails) {
handler.post(new Runnable() {
#Override
public void run() {
methodResult.error(errorCode, errorMessage, errorDetails);
}
});
}
#Override
public void notImplemented() {
handler.post(new Runnable() {
#Override
public void run() {
methodResult.notImplemented();
}
});
}
}
#Override
public void onMethodCall(MethodCall call, Result rawResult) {
Result result = new MethodResultWrapper(rawResult);
if (call.method.equals("extractText")) {
final String tessDataPath = call.argument("tessData");
final String imagePath = call.argument("imagePath");
String DEFAULT_LANGUAGE = "eng";
if (call.argument("language") != null) {
DEFAULT_LANGUAGE = call.argument("language");
}
calculateResult(tessDataPath, imagePath, DEFAULT_LANGUAGE, result);
} else {
result.notImplemented();
}
}
private void calculateResult(final String tessDataPath, final String imagePath, final String language,
final Result result) {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
final String[] recognizedText = new String[1];
final TessBaseAPI baseApi = new TessBaseAPI();
baseApi.init(tessDataPath, language);
final File tempFile = new File(imagePath);
baseApi.setPageSegMode(DEFAULT_PAGE_SEG_MODE);
baseApi.setImage(tempFile);
recognizedText[0] = baseApi.getUTF8Text();
baseApi.end();
result.success(recognizedText[0]);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
}.execute();
}
}
By using join you're making the main thread wait for the background thread, blocking it. You have to remove the join and return a result immediately.
So, how do you return the ocr result, which won't be available immediately. When it becomes available, you then call a method from native to dart, passing the result. At the dart end, you then handle the result as any async event.
The point of the last paragraph of your question is that your result will become available on your background thread, so you'd want to call the native to dart method there. You can't. You have to post the method call code to the main looper - you already show some code for posting to the main looper which you can use as an example.
Based on Richard Heap answer I came up with this:
Dart code:
_channel.setMethodCallHandler((call) {
switch (call.method) {
case "extractTextResult":
final String result = call.arguments;
print(result);
}
var t;
return t;
});
Java code:
Thread t = new Thread(new Runnable() {
public void run() {
baseApi.setImage(tempFile);
recognizedText[0] = baseApi.getHOCRText(0);
baseApi.end();
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
channel.invokeMethod("extractTextResult", recognizedText[0]);
}
});
}
});
t.start();
result.success("tst");
explain:
This code will run the Java extractText in a separate thread, and when the result is ready it will hopp back to the ui thread with the call to Looper.getMainLooper() which will then send the message back to the Dart side which must receive the message on the ui thread, which is what this message means:
**Channels and Platform Threading**
Invoke all channel methods on the platform’s main thread when writing code on the platform side.
NOTE on the Dart side, this is still incomplete example since you then need to report to the ui that a message received, this can be done with a Completer, which is used to create and complete a future
At the end of your method channel just return the response back to dart side
Add this line at the end of method channel result.success(true)
full example
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"method-channel"
).setMethodCallHandler { call, result ->
if (call.method == "getFirebaseAppCheckDebugToken") {
...
result.success(true) // just add this line
}
}
}```

Javafx Updating UI from a Thread Java 8

I interested in one interesting task. I have UI in JavaFx with another thread which updates UI. I started updates from Platform.runLater. Code:
private void startUpdateDaemon() {
updateUserStatus();
updateTable();
}
private void startUpdateDaemonTask() {
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
while (true) {
Platform.runLater(() -> {
startUpdateDaemon();
});
Thread.sleep(1000);
}
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
#Override
public void initialize(URL location, ResourceBundle resources) {
startUpdateDaemonTask();
}
Also I have place in another class where I updates UI:
private void startUpdateDaemonTask() {
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
while (true) {
Platform.runLater(new Runnable() {
#Override
public void run() {
updateGameStatus();
}
});
Thread.sleep(1000);
}
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
So, finally I have two places with call "Platform.runLater" and different methods inside.
My question is Can I create only "one" method with one time call "Platform.runLater" and send to this method different methods which will be call ?? May be I can write finish method with consumers and send to him methods 'startUpdateDaemon()' and 'updateGameStatus()'?
Thanks a lot.
You can add a Runnable parameter to your method. This parameter is given to you Platform.runLater:
private void startUpdateDaemonTask(Runnable runner) {
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
while (true) {
Platform.runLater(runner);
Thread.sleep(1000);
}
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
Now you can invoke this method with your method references:
startUpdateDaemonTask(this::startUpdateDaemon);
startUpdateDaemonTask(this::updateGameStatus);

Replace a TableView with a ProgressIndicator within VBox JavaFX

I have a TableView associated with some data, and once i hit a run button i perform some processing on that data. Each row of data is handled in a seperate thread, and while those threads are running i want a ProgressInducator to replace the table within its vbox.
In the attached code:
If I stop where is says "WORKS IF STOP HERE" - table is replaced with pi.
If I continue waiting for the threads to join - no replacing.
What am I missing?
runButton.setOnAction(
new EventHandler<ActionEvent>() {
#Override
public void handle(final ActionEvent e) {
List<Thread> threadList = new ArrayList<Thread>();
int threadCounter = 0;
final ProgressIndicator pi = new ProgressIndicator(threadCounter);
vbox.getChildren().clear();
vbox.getChildren().addAll(pi);
for (ProductInTable product : data) {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try
{
product.calculate();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
});
threadList.add(thread);
thread.start();
}
int x = threadList.size();
/** WORKS IF STOP HERE **/
// wait for all threads to end
for (Thread t : threadList) {
try {
t.join();
threadCounter++;
pi.setProgress(threadCounter / x);
} catch (InterruptedException interE) {
interE.printStackTrace();
}
}
/** DOESNT WORKS IF STOP HERE **/
Thread.join() blocks execution until the thread is completed. Since you are calling this on the FX Application Thread, you block that thread until all your worker threads finish. This means the UI is unable to update until those threads are complete.
A better approach is probably to represent each computation with a task, and update a counter of complete tasks back on the FX Application Thread using setOnSucceeded. Something like:
runButton.setOnAction(
new EventHandler<ActionEvent>() {
#Override
public void handle(final ActionEvent e) {
final ProgressIndicator pi = new ProgressIndicator(threadCounter);
vbox.getChildren().clear();
vbox.getChildren().addAll(pi);
final int numTasks = data.size();
// only access from FX Application thread:
final IntegerProperty completedTaskCount = new SimpleIntegerProperty(0);
pi.progressProperty().bind(completedTaskCount.divide(1.0*numTasks));
completedTaskCount.addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> obs, Number oldValue, Number newValue) {
if (newValue.intValue() >= numTasks) {
// hide progress indicator and show table..
}
}
});
for (final ProductInTable product : data) {
Task<Void> task = new Task<Void>() {
#Override
public Void call() {
try
{
product.calculate();
} catch (IOException ioe) {
ioe.printStackTrace();
}
return null ;
}
});
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
completedTaskCount.set(completedTaskCount.get()+1);
}
});
new Thread(task).start();
}
}
});
If you potentially have a large number of items here, you should use some kind of ExecutorService instead to avoid creating too many threads:
ExecutorService exec = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors()); // for example...
and then replace
new Thread(task).start();
with
exec.submit(task);

Setting a label in a thread causes IllegalStateException

I have a thread like:
startButton.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field arg0, int arg1) {
Thread thread = new Thread(){
public void run() {
uploadFile();
}
};
thread.start();
}
//});
});
The uploadFile method contains the line label_up_result.setText(result); which causes an IllegalStateException.
label_up_result is defined like: final LabelField label_up_result=new LabelField("", LabelField.FIELD_LEFT);
What can be the problem ? How can I fix it ?
The problem is probably that you are trying to update the UI from a worker thread. There are two approaches. You can synchronize on the event lock:
synchronized(UiApplication.getUiApplication().getEventLock())) {
label_up_result.setText(result);
}
or you can create a Runnable to execute on the UI thread:
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
label_up_result.setText(result);
}
});
I don't know about blackberry, but usually you need to perform the ui-actions in the ui-thread. SwingUtilities.invokeLater provides that functionality in JavaSE, http://www.java2s.com/Code/Java/Swing-JFC/Swinginvokelater.htm

Resources