How do I code multithreading to speed up heavy, repetitive task? - java

I have a swing application that is quite slow to start up because it has to load a thousand pictures into the GUI. It takes 10 seconds to start up.
It is a single thread application, how could I code multithread to speed up the task? The following code is in a for loop of 1000 iterations.
ImageIcon icon = new ImageIcon(Files.readAllBytes(coverFile.toPath()));
// ImageIcon icon = createImageIcon(coverFile);
JLabel label = null;
if (coverCount % 2 == 0) {
label = createLabel(coverFile, movieFile, icon, SwingConstants.LEFT);
} else {
label = createLabel(coverFile, movieFile, icon, SwingConstants.CENTER);
}
box.add(label);
The images are being loaded and put into a Box sequentially. I have two difficulties if I want to do it multithread
How does a thread return value to parent
How to achieve non-blocking call back which add the image to the
box sequentially
Thank you.

How does a thread return value to parent
Use a call-back mechanism. For Swing that would mean using a SwingWorker and notifying the GUI of thread completion either in the worker's done() method or by adding a PropertyChangeListener to the worker, listening to the worker's "state" property, for when it changes to SwingWorker.StateValue.DONE
How to achieve non-blocking call back which add the image to the box sequentially
The SwingWorker has a publish/process method pair that allows sending data sequentially from the background thread via the publish method, and then handle the data sequentially on the event thread within the process method. This requires use of a SwingWorker<VOID, Image> or SwingWorker<VOID, Icon> or something similar, the 2nd generic parameter indicating the type of object sent via this mechanism.
For example:
public class MyWorker extends SwingWorker<Void, Icon> {
#Override
protected Void doInBackground() throws Exception {
boolean done = false;
while (!done) {
// TODO: create some_input here -- possibly a URL or File
Image image = ImageIO.read(some_input);
Icon icon = new ImageIcon(image);
publish(icon);
// TODO: set done here to true when we ARE done
}
return null;
}
#Override
protected void process(List<Icon> chunks) {
for (Icon icon : chunks) {
// do something with the icon here
// on the event thread
}
}
}
And to use it within a GUI:
// may need constructor to pass GUI into worker
final MyWorker myWorker = new MyWorker();
myWorker.addPropertyChangeListener(evt -> {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
// We're done!
// call get to trap errors
try {
myWorker.get();
} catch (InterruptedException | ExecutionException e) {
// TODO: real error handling needed here
e.printStackTrace();
}
}
});
myWorker.execute(); // start worker in a background thread
For more on Swing concurrency, please check out Lesson: Concurrency in Swing

Multithreading will speed up the application but I think doing a lazy load is a better approach (you can do both). You can't be displaying all these images at the same time so I suggest you load the images that will be visible at the start and after that load the image as needed this will hugely increase your performance and use less memory/resource.

If you really want to load all 1000 images:
It is enough to use one background thread, so that you don't slow down the main Swing Event loop thread.
Create a custom class which implements runnable, and has references to all the context to do the job. Like so:
public static class IconLoader implements Runnable{
private List<File> movies;
private File coverFile;
private JPanel box;
public IconLoader(JPanel box, File coverFile, List<File> movies) {
this.box = box;
this.coverFile = coverFile;
this.movies = movies;
}
#Override
public void run() {
for(int coverCount=0;coverCount<movies.size();coverCount++) {
try {
final JLabel label;
File movieFile = movies.get(coverCount);
ImageIcon icon = new ImageIcon(Files.readAllBytes(coverFile.toPath()));
// ImageIcon icon = createImageIcon(coverFile);
if (coverCount % 2 == 0) {
label = createLabel(coverFile, movieFile, icon, SwingConstants.LEFT);
} else {
label = createLabel(coverFile, movieFile, icon, SwingConstants.CENTER);
}
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
box.add(label);
}
});
}catch(IOException e) {
e.printStackTrace();
}
}
}
private JLabel createLabel(File coverFile, File movieFile, ImageIcon icon, int direction) {
//Create the label and return
return null;
}
}
Then start the loading process during your app initialization, by passing the runnable to a new thread, and starting the thread. Like so:
new Thread( new IconLoader(box, coverFile, movies) ).start();

Related

Progress Bar for a GUI application using Java

I am trying to write a progress bar for an application that is downloading information before the GUI runs. Because this is such a long process to download and organize the information, I am wanting to inform the user of the progress. I decided on using a progress bar late in the game and, as such, a majority of the code is written and I'm trying to incorporate the progress bar into the code without a drastic re-working of the code. The following is the code for the progress bar. Currently, the progress bar comes up AFTER everything runs and the GUI pops up.
static class PopulatingCardsWorker extends SwingWorker<Void, Integer> {
JProgressBar jpb;
int max;
JLabel label;
public PopulatingCardsWorker(JProgressBar jpb, int maximum, JLabel label) {
this.jpb = jpb;
this.max = maximum;
this.label = label;
}
#Override
protected void process(List<Integer> chunks) {
int i = chunks.get(chunks.size()-1);
jpb.setValue(i); // The last value in this array is all we care about.
System.out.println(i);
label.setText("Loading " + i + " of " + max);
}
#Override
protected Void doInBackground() throws Exception {
for(int i = 0; i < max; i++) {
Thread.sleep(10); // Illustrating long-running code.
publish(i);
}
return null;
}
#Override
protected void done() {
try {
get();
JOptionPane.showMessageDialog(jpb.getParent(), "Success", "Success", JOptionPane.INFORMATION_MESSAGE);
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}
}
private static void go(int max) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JLabel label = new JLabel("Loading...");
JProgressBar jpb = new JProgressBar();
jpb.setIndeterminate(false);
jpb.setMaximum(max);
panel.add(label);
panel.add(jpb);
frame.add(panel);
frame.pack();
frame.setSize(200,90);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
new PopulatingCardsWorker(jpb, max, label).execute();
}
The program initially calls the GUI application and then runs the database acquisition code as shown below.
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new MagicTheGatheringUI().setVisible(true);
}
});
}
public MagicTheGatheringUI() {
int i, size;
colorRefinementFilter = "selected";
try {
my_list=new CardDatabase();
my_list.sortByName(my_list.all_cards);
my_list.populateSubArrays();
size = my_list.all_cards.size();
for(i = 0; i < size; i++)
{
namesList.addElement(my_list.all_cards.get(i).name);
}
} catch(Exception e) {
System.out.println(e);
System.exit(0);
}
initComponents();
}
The swing worker is created during the creation of "my_list=new CardDatabase();". In that class, I have the swing worker and the process the swing worker is supposed to monitor.
I currently call swing worker in a method called "populate_cards()" and I use the following code to create the swing worker. The swing worker is supposed to monitor what's going on in the populate_cards() method. All of the data in the swing worker methods are just temporary until I better understand how to make it work the way I want it to.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
go(1000);
}
});
I believe the issue is that I'm calling the progress bar inside of the "invokeLater" method for the actual GUI.
I have looked at the following questions to try and solve my problem.
How to add a progress bar?
Can a progress bar be used in a class outside main?
and I have also looked at tutorials.
Your code is ok. The reason why the progressBar (and the gui itself) pops after the code ends is that by default in java, the code and the gui runs on the same Thread. Because the code has higher priority then the gui, all the code executes first and after that the gui updates.
You should do that in the "go" method:
new Thread(){
public void run(){
new PopulatingCardsWorker(jpb, max, label).execute();
}
} start();
see that article for more information about threads
Im answering from my phone, so sorry about the bad writing.
Code and GUi threads
and from Wikipedia
last one

Jslider can not tick when its value changed from JButton periodically

I am working on a swing project. There is a map, I have raster images of a given data for different times. Normally I change the time via a JSlider and it requests server for raster image. Then I add response image to map. There is a Play JButton, when pressed it will add those images one by one to raster layer of the map. It will be seen as an animation. In that JButton's actionPerfomed method I change the JSlider's value in a for loop.
My problem is when I press Play JButton, I can't see the data is played but I know code block works because I record each image(from server). I found out that it is becuse of JButton does not release Focus until its actionPerformed method ends. Because JButton looked like it was pressed until the end. So I only see the last image in the map.
First, I tried JButton.setFocusable(false) etc. but to no good.
Second, I tried using SwingWorker. I added it like this:
class PlayMvgmLayerWorker extends SwingWorker<Void, Void> {
public PlayMvgmLayerWorker(String title) {
super(title);
}
#Override
protected void done(Void aVoid) {
}
#Override
protected Void doInBackground() {
try{
BufferedImage[] image = new BufferedImage[24];
for(int i=0; i<24; i++) {
final int value = i - 48 + 24;
timeSlider.setValue( value );
Thread.sleep(10000l);
}
} catch(Exception ex) {
ex.printStackTrace();
}
return null;
}
}
private JButton animation = new JButton("");
animation.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new PlayMvgmLayerWorker("title").execute();
}
});
private JSlider timeSlider = new JSlider();
timeSlider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
// time consuming processes ( server call, add image to map, etc)
}
});
I tried to simplify it.
It is much better than before, but I still can not see the data played properly. Sometimes data is played after JSlider ticks. Could it be because my time consuming process is in the second components(JSlider) stateChanged event? Should I use a second SwingWorker in JSlider's event too? Any suggestions about what can I do?
Moreover, what would be the best way to disable all components before playing data, and enable them after playing data?
Thank you very much in advance
If you have two activities Activity A and Activity B which have to be run simultaneously, you need to create a thread for the second activity - the first activity will already be run in its own thread (the main program).
The scheme is as follows:
Program A:
create new Thread: Activity B
run allother tasks for Activity A
In more specific terms the following program will run your simulation and update the tick of the slider:
public P() {
animation.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
doInBackgroundImp();
}
});
setSize(500, 500);
setLayout(new FlowLayout());
add(animation);
add(timeSlider);
setVisible(true);
}
protected void doInBackgroundImp() {
Thread th=new Thread() {
public void run() {
try{
for(int i=0; i<24; i++) {
final int value = i - 48 + 24;
timeSlider.setValue( i );
System.out.println(timeSlider.getValue()+" "+value);
Thread.sleep(1000);
}
} catch(Exception ex) {
ex.printStackTrace();
}
}
};
th.start();
}
private JButton animation = new JButton("");
private JSlider timeSlider = new JSlider();
}

JLabel disappears after a setText()

I'm setting up a mock program in an MVC fashion with Swing. I'm having troubles calling setText on a JLabel from an external Thread. I know Swing has its own threading model (setText is thread-safe though), so I've also tried forcing the setText call to be carried out by the EDT via SwingUtilities functions, with no results.
This is what the classes look like - I've omitted the irrelevant chunks, suppose they have references to each other and the GUI's layout is properly set up:
public class View extends JFrame
{
private final JLabel label = new JLabel("idle");
private final JButton button = new JButton("press me");
public View(final Controller controller)
{
this.controller = controller;
button.addListener(e -> controller.input());
}
public void refresh(final boolean value)
{
label.setText(value ? "YEP" : "NOPE");
}
}
public class Controller
{
private final ExecutorService service =
Executors.newSingleThreadExecutor();
public void input()
{
service.submit((Runnable) () ->
{
try
{
view.refresh(true);
Thread.sleep(1000);
}
catch(final InterruptedException exception)
{
exception.printStackTrace();
}
finally
{
view.refresh(false);
}
});
}
}
Expected behavior:
Button is pressed
input() is called on controller
A new thread is created, which first sets label's text to "YEP"
The newly created thread simulates some delay with Thread.sleep()
It awakens and sets label's text to "NOPE"
What I get is that after the call at point 3 the label disappears, but is drawn again at 5, showing the text "NOPE".
What am I doing wrong?

Communication between Java Swing Application and Leap Motion Listener

I want to know how I can do a communication between the Java Swing Application and between my leap motion listener.
Because I want that in my application when I click on a button I can change a number with the number of finger see by the leap motion.
I have one Java Swing application :
public class KidCountingFrame extends JFrame
And one Leap Motion Runnable:
public class LeapMouse implements Runnable
{
#Override
public void run()
{
CustomListener l = new CustomListener();
Controller c = new Controller();
c.addListener(l);
}
}
Which is launching a Leap Motion Listener... :
public class CustomListener extends Listener
Maybe I have to use a design pattern ?
* UPDATE : *
I try to applied the ProgressBarDemo on my project and to follow explications.
But one error happens when I put the listener in the SwingWorker constructor :
Exception in thread "Thread-1443" java.lang.NullPointerException: null upcall object
Here my updated code :
public class PanelLeapFingers extends JPanel implements ActionListener,
PropertyChangeListener
{
private JButton digitDisplayButton;
private Task task;
class Task extends SwingWorker<Void, Void>
{
public Task()
{
try
{
NbFingersListener l = new NbFingersListener();
Controller c = new Controller();
c.addListener(l);
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
}
#Override
public Void doInBackground()
{
// In background :
// Find how digit fingers are shown by the user
int progress = 0;
//setProgress(????); //I don't really know how call the listener here
setProgress(5); //Here it's just to make a test
return null;
}
#Override
public void done()
{
Toolkit.getDefaultToolkit().beep();
digitDisplayButton.setEnabled(true);
setCursor(null); // turn off the wait cursor
}
}
public PanelLeapFingers()
{
super(new BorderLayout());
digitDisplayButton = new JButton("?");
digitDisplayButton.setFont(new Font("Arial", Font.PLAIN, 40));
digitDisplayButton.setActionCommand("start");
digitDisplayButton.addActionListener(this);
JPanel panel = new JPanel();
panel.add(digitDisplayButton);
add(panel, BorderLayout.PAGE_START);
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
}
public void actionPerformed(ActionEvent evt)
{
digitDisplayButton.setEnabled(false);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
task = new Task();
task.addPropertyChangeListener(this);
task.execute();
}
public void propertyChange(PropertyChangeEvent evt) {
if ("progress" == evt.getPropertyName()) {
int progress = (Integer) evt.getNewValue();
digitDisplayButton.setText(progress+"");
}
}
}
I'm not sure to be on the good way and I don't understand how I can receive information from my listener in my setProgress( ) function.
EDIT :
Solution : Finally I have decide to use a Singleton Model to communicate between the listener and the Java Swing APP. I save all informations when the Listener is working in the Singleton Model and I recover the information that I need in the Java Swing APP.
As discussed here, the Listener will be called asynchronously, typically from another thread. To avoid blocking the event dispatch thread, create your Listener in the constructor of a SwingWorker and arrange for your doInBackground() implementation to publish() frames of interest; process() can then handle these frames on the event dispatch thread.
Alternatively, poll the Controller at a suitable rate in the ActionListener of javax.swing.Timer.

JLayer Player overwrites refesh of SWT widgets when placed in a loop

I am attempting to make an application that retrieves images and .mp3 files and transitions from one image to the next once the audio has finished. The underlying framework of how I transition between these images is a little convoluted, but I have managed to get an action in SWT that successfully enables me to manually transition from one to the next. However, a problem has arisen when I've tried to automate it; when placed into a loop, my playAudio() method begins before all of the calls I make in my displayShow() method have resolved, which results in a blank window, despite the audio still playing.
Here is the run method for the action that I want to start the show:
Action startAction = new Action("Start") {
public void run() {
//do {
displayShow();
playAudio();
//} while(true);
}
};
Here is playAudio(). I am able to PLAY the audio without incident:
public void playAudio() {
final String audio = "Happy_Birthday.mp3";
audioLatch = new CountDownLatch(1);
audioThread = new Thread() {
public void run() {
try {
Player player = new Player
(new BufferedInputStream
(new FileInputStream(audio)));
player.play();
audioLatch.countDown();
} catch (JavaLayerException e) {
} catch (FileNotFoundException e) {
} catch (Exception e) {
}
}
};
audioThread.start();
try {
audioLatch.await();
} catch (InterruptedException e) {
}
}
And here is displayShow():
private void displayShow() {
new Thread() {
public void run() {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
Control[] children = container.getChildren();
for (int i = 0; i < children.length; i++) {
children[i].dispose();
}
show.showSlide(container);
container.layout();
}
});
}
}.start();
}
show.showSlide returns a composite whose parent is container, which is the immediate child of the highest parent composite. Within the newly created composite, an image is added to a label and the label's parent is assigned to composite. I realize whether displayShow() is in a separate thread or not seems to be immaterial; this was just the last thing I tried.
It is not solely the addition of the loop that causes the refresh to not execute. The only way I can get the manual transition to work is if I remove the CountDownLatch from the playAudio() method. Were I to remove this latch, the only way to encase these two methods in a loop would be embedded while loops, which seem to hog a fair amount of the CPU and still does not solve my problem. Am I missing anything?
The audioLatch.await() is blocking the main program thread, this is the thread that all SWT operations run on so the Display.asyncExec runnables are just being queued until the thread is available.
If you really must wait in the playAudio method you could run the display event loop there until the background thread is finished:
while (! background thread finished)
{
if (!display.readAndDispatch())
display.sleep();
}

Categories

Resources