I have an application which is quite resource intensive, it is using large images as input and some of the operations on these images can take a while. I am therefore looking to make some parts run in their own threads. To do this I have used the following code just to test out first:
Thread t1 = new Thread(new Runnable() {
public void run()
{
inputChooser.setFileFilter(filter);
inputChooser.addChoosableFileFilter(filter);
int img = inputChooser.showOpenDialog(this);
if (img == JFileChooser.APPROVE_OPTION) {
File file = inputChooser.getSelectedFile();
String filename = file.getName();
if (filename.contains("B10")) {
greenBand = 1;
}
if (filename.contains("B20")) {
greenBand = 2;
}
if (filename.contains("B30")) {
greenBand = 3;
}
if (filename.contains("B40")) {
greenBand = 4;
}
if (filename.contains("B50")) {
greenBand = 5;
}
if (filename.contains("B60")) {
greenBand = 6;
}
if (filename.contains("B70")) {
greenBand = 7;
}
try {
greenImage = ImageIO.read(file);
ImageIO.write(greenImage, "JPEG", new File("img2_tmp.jpeg"));
greenImage = ImageIO.read(new File("img2_tmp.jpeg"));
if (greenImage.getWidth() > 8000 | greenImage.getHeight() > 7000) {
greenImage = greenImage.getSubimage(1450, 1400, (greenImage.getWidth()-3200), (greenImage.getHeight()-3000));
}
update(greenImage, greenIcon, greenLabel);
loadingBar.setIndeterminate(false);
checkInput();
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "Input Image Error", "Input Error", WARNING_MESSAGE);
}
}
}});
t1.start();
When I run the application it freezes when this code is called. However, I have managed to get it to work once, I am not sure how but it ran perfectly (not the first time, it froze a few times first and then randomly worked one time). I haven't changed any of the code just some of the indents and such to get it to fit with the rest of the code and ever since it just continues to freeze. A button action press calls this method where the above code is, as soon as the buttons pressed it freezes.
Is there a reason as to why this is happening?
Thanks in advance
You are calling a non-thread-safe code (swing (is NOT thread safe)) from both threads (newly created and main thread) at the same time.
Make sure that you have decoupled the logic before creating new threads.
For this specific use case, I'd suggest that you use SwingWorkers in stead of threads, they are easy to use, and work well within the limitations of swing.
More about SwingWorkers at http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
Hope this helps.
Good luck.
It's difficult to say exactly, but I notice that the variable greenImage and greenBand are not declared anywhere. That makes me think they are global variables. If something else has access to them, it's possible that they're causing some manipulation that sends your code into an infinite loop or does other unexpected Bad Things.
Related
I'm trying to create something like an info screen that just scrolls through images (and maybe one day pdfs) that are uploaded with a small managment app I wrote.
After some reading I think I understand why I should NOT have Thread.sleep in my for loop. I read some posts on stackoverflow and other pages that teached me not to do it.
Apparently I'm supposed to use ExecutorService for something like this, but I can't wrap my head around it.
So after preparing my GUI and everything else I finally call my showImages and stay in this while loop.
After each loop I reload the records from the file systems, so any changes are represented on the screen.
I created a metafile containing the preferred delay for each image and just sleep for the amount of time.
private void showImages() {
//noinspection InfiniteLoopStatement
while (true) {
loadRecords();
for (ImageRecord record : records) {
System.out.println("Showing: " + record.getFilename());
imageLabel.setIcon(record.getIconForInfoScreen(screenWidth, screenHeight));
int delay = record.getDurationAsInt();
System.out.println("Waiting for " + delay + " milliseconds");
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
So if I understand correctly I could use something like
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(showImage(),0, delay, TimeUnit.MILLISECONDS);
But this would trigger the showImage() every "delay" millisecond and I still need to increment some counter, to get to the next image in my records.
Could someone push me in the right direction? I'm kinda lost at the moment.
All the comments on my question can be considered good ideas an and one of them also lead me to my final result. But it in the end, the matter was, that I did not understand how to implement a timer into my loop.
Or in other words, the loops had to be gone and instead use a counter and set the delay for the follow up record inside the timer loop.
private void loopImages() {
loadRecords();
recordSize = records.size();
int delay = records.get(1).getDurationAsInt();
timer = new Timer(delay, e -> showImages());
timer.start();
}
private void showImages() {
if (recordIndex == recordSize) {
loadRecords();
recordSize = records.size();
recordIndex = 0;
}
ImageRecord record = records.get(recordIndex);
imageLabel.setIcon(record.getIconForInfoScreen(screenWidth, screenHeight));
recordIndex++;
if (recordIndex < recordSize) {
record = records.get(recordIndex);
int delay = record.getDurationAsInt();
timer.setDelay(delay);
}
}
public class PrimeFinder implements Runnable {
Thread go;
StringBuffer primes = new StringBuffer();
int time = 0;
public PrimeFinder() {
start();
while (primes != null) {
System.out.println(time);
try {
Thread.sleep(1000);
} catch(InterruptedException exc) {
// do nothing
}
time++;
}
}
public void start() {
if (go == null) {
go = new Thread(this);
go.start();
}
}
public void run() {
int quantity = 1_000_000;
int numPrimes = 0;
// candidate: the number that might be prime
int candidate = 2;
primes.append("\nFirst ").append(quantity).append(" primes:\n\n");
while (numPrimes < quantity) {
if (isPrime(candidate)) {
primes.append(candidate).append(" ");
numPrimes++;
}
candidate++;
}
System.out.println(primes);
primes = null;
System.out.println("\nTime elapsed: " + time + " seconds");
}
public static boolean isPrime(int checkNumber) {
double root = Math.sqrt(checkNumber);
for (int i = 2; i <= root; i++) {
if (checkNumber % i == 0) {
return false;
}
}
return true;
}
public static void main(String[] arguments) {
new PrimeFinder();
}
}
As the title states I do not get the end result of the program in the console... The timer does count, until the calculation is done, but when the program is supposed to print out the result, the console clears and goes all blank. I tried to enter my code in some kind of online compiler, and sure enough, I got the end result printed out. Has anyone had similar problem and if so, how did you manage to fix it? Thanks in advance!
I tried this in IntelliJ and the output is retained in the console - though it's not all retained when using Eclipse as you point out. Looks like Eclipse maybe overwrites the console upon switching threads.
You could just write the output to a file and view it there, although this is of course different from retaining it in the console. To do that you would go to Run > Run Configurations..., then select your application under Java Application, then click the Common tab, then under Standard Input and Output check the Output file checkbox and enter a log file path and file name for where you want logs to go, then Apply. Then run that "Run Configuration" and view the output in the file.
If running on a server (e.g., tomcat) from within Eclipse, you would go to the Server view, double-click your server, click Open launch configuration, then Common, then Output file. From there you can specify an output destination on your file system or in your Eclipse workspace.
If you started the Thread instead of just doing the instantiation, maybe it would print something out.
Thread thread = new Thread(new PrimeFinder());
thread.start();
So I've done a lot of searching and I can't quite find a solution to the problem I'm having. Designing GUIs with Java's JFrame seems fairly straight-forward, but updating them visually when it's more than just a setText call seems much more tricky, especially when you want to simulate some wait time for visual recognition in between. I'm not familiar with multi-threading and all of the solutions I found, I couldn't seem to get to work how I needed them to.
What I'm trying to do is to simulate dice rolling before landing on a particular side for a simple dice game. To do this, I'm just switching between different sides of dice images a set number of times (10, for example) every fraction of a second (150 ms in this case).
The interesting thing is that this code at its current state works EXACTLY how I want it to on start-up (which does a full 10 roll cycle once before accepting any user-input, but every component of the GUI is visible and working as expected). The problem is that when re-rolling, the GUI goes blank until all the thread sleeping is completed.
With my lack of knowledge about how this sort of thing, I don't understand why it would work the way I want the first time, but not any time after. Hopefully I can get some assistance about the right way to accomplish this or a fix for what I'm currently doing. Thanks!
private void rollDice()
{
for(int i = 0; i < 10; i++)
{
dice1 = (int) (Math.random() * 6) + 1;
dice2 = (int) (Math.random() * 6) + 1;
img1 = new ImageIcon(dice1 + ".png");
img2 = new ImageIcon(dice2 + ".png");
try
{
Thread.sleep(150);
}
catch(Exception e)
{
}
lbl1.setIcon(img1);
lbl2.setIcon(img2);
}
}
Try this I found here.
new Thread(new Runnable
{
public void run()
{
for(int count = 0; count < PREROLLS; count++)
{
//Add your for loop logic here!
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
//then update you icon here. I would start with one die if I were you.
imgDice1 = new ImageIcon(path + dice1 + ".png");
}
});
try { Thread.sleep(100); } catch(Exception e) {}
}
}
}).start();
After getting smacked around a little over here I went and grabbed the YourKit Java Profiler and got it nicely integrated with Eclipse. It's shown me some things about this application but now I'm running into limitations due to my simple lack of experience with Java.
The first chunk of code that's reporting high CPU usage (~49%) is the following. This reads strings over a sockets connction and hands them off. That all seems to work fine. However, the call to Runtime.freeMemory() is reporting 41% CPU usage all by itself. Why is that?
protected void PerformGarbageCollection()
{
long oldMem = m_runtime.freeMemory();
if (oldMem < 4000000)
{
m_runtime.gc();
m_runtime.freeMemory();
}
}
#Override
public void run()
{
ByteBuffer.allocate(BUFFER_SIZE);
int loopCounter = 0;
do
{
if (loopCounter++ > 10)
{
PerformGarbageCollection();
loopCounter = 0;
}
try
{
//reading stuff...
}
catch (Throwable e)
{
System.out.println(e.getClass().getName() + " EXCEPTION in read loop: " + e.getMessage());
}
}
while (true);
}
My first impression is that the second call to Runtime.freeMemory() is useless. After that, my impression is that this means of cleaning up after oneself is really crude. Is there some quirk of Java that I'm missing here, or is this just sloppy? And, more importantly, what might be the 'right' way to do this?
If I just disable that garbage collection, I wind up with EventDispatchThread.run() claiming 71% of the CPU. When I drill down to the first Java system call in the profiler, I find Window.setVisible() using 7% of the CPU all by its lonesome.
public final void CreateOTable(int x, int y, int xx, int yy, String suffix)
{
JFrame oframe = new JFrame(OTable.title);
OTable ot = new OTable(oframe);
ot.setOpaque(true);
ot.SetTScreen(m_TScreen);
oframe.setContentPane(ot);
oframe.setJMenuBar(new OTableMenu(ot));
LoadOTable(ot, x, y, xx, yy, suffix);
oframe.setSize(x, y);
oframe.setLocation(xx, yy);
//==========================
oframe.setVisible(true); //This line
//==========================
ScreenListener.otv.add(ot);
oframe.addWindowListener(owner);
}
Again, I'm not sure what's going on here. Setting a UI element to visible should just be a once-and-done function call, right?
Similarly, the below call to JOptionPane.showInputDialog() is pulling 28% even after the dialog has been used to load the config file.
String filename = config_file;
if (filename == "")
{
filename = JOptionPane.showInputDialog("Please enter config file.");
if (filename == null)
filename = "";
}
What's really going on here? None of this seems like it should be devouring system resources, but my end users report sluggish GUI responses. Can anyone point me in the right direction?
I am using NetNeans
I have a JPanel that is supposed to show some already input data along with a progress bar while the remainder of the program carries out some lengthy processing (about 1 minute long). Once the processing is done, this same JPanel is supposed to be updated with a few JTextFields containing the processed data.
The issue:
The JPanel shows up completely blank throughout the time period the processing is going on. Once the processing is done, then all the data shows up. Its baffling me. Here is how i've programmed it:
/*The parent class which calls the output JPanel
*Lister is the output JPanel. The constructor calls initComponents(), and fills
*text fields that contain the data that has already been input.
*fillresult then triggers the processing function that takes a while. Once the
*processing function is done, fillresult fills the processed data into the
*respective Text Fields
*/
ListWords = new Lister();
System.out.println("l");
ListWords.setVisible(true);
ListWords.fillResult();
//Lister constructor:
Lister(){
initComponents();
MatchWords = new Matcher();//Matcher() only contains the initialization of a List
jTextField1.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(0, 1));
jTextField2.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(1, 2));
jTextField3.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(2, 3));
jTextField4.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(3, 4));
jTextField5.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(4, 5));
jTextField6.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(5, 6));
jTextField7.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(6));
}
//fillresult():
public void fillResult(){
int rIndex = 0;
MatchWords.makeWords(rIndex);
while(MatchWords.realWords.Head == null && rIndex < 2){
rIndex++;
updateStatusBar();
MatchWords.makeWords(rIndex);
}
Wordpoints = new String[MatchWords.realWords.getSize()];
for(int i=0; i<Wordpoints.length; i++){
Wordpoints[i] = "";
}
for(int i=0; i<MatchWords.realWords.getSize(); i++){
int total = 0;
for(int j=0; j<MatchWords.realWords.getNode(i).getWord().length(); j++){
for(int k=0; k<Scrabbulous.scrab.scralphabet.length; k++){
if(MatchWords.realWords.getNode(i).getWord().charAt(j) == Scrabbulous.scrab.scralphabet[k].getLetter()){
total += Scrabbulous.scrab.scralphabet[k].getValue();
Wordpoints[i] = ""+total;
}
}
}
}
try{
jTextField8.setText(MatchWords.realWords.Head.getWord());
jTextField13.setText(Wordpoints[0]);
}catch(NullPointerException e){
jTextField8.setText("No Match Found");
jTextField13.setText("-");
}
try{
jTextField9.setText(MatchWords.realWords.getNode(1).getWord());
jTextField14.setText(Wordpoints[1]);
}catch(NullPointerException e){
jTextField9.setText("No Match Found");
jTextField14.setText("-");
}
try{
jTextField10.setText(MatchWords.realWords.getNode(2).getWord());
jTextField15.setText(Wordpoints[2]);
}catch(NullPointerException e){
jTextField10.setText("No Match Found");
jTextField15.setText("-");
}
try{
jTextField11.setText(MatchWords.realWords.getNode(3).getWord());
jTextField16.setText(Wordpoints[3]);
}catch(NullPointerException e){
jTextField11.setText("No Match Found");
jTextField16.setText("-");
}
try{
jTextField12.setText(MatchWords.realWords.getNode(4).getWord());
jTextField17.setText(Wordpoints[4]);
}catch(NullPointerException e){
jTextField12.setText("No Match Found");
jTextField17.setText("-");
}
Could someone please be of assistance? Oh yes, and I read about adding a .refactor() and/or repaint(), but atleast the jForms initialized in the constructor should show up? :/
Also, it might be worth mentioning that all the code that is currently in the fillresult() function was initially in the constructor, and the output was displaying. However, since I want the progressbar here, I've shifted it to a new function. help please?
"The issue: The JPanel shows up completely blank throughout the time period the processing is going on. Once the processing is done, then all the data shows up."
What's happening is that everything is occurring on one thread, the Event Dispatch Thread (EDT). In order for an event to occur, like your JPanel updating, the process before it needs to complete.
As stated about the EDT:
" Tasks on the event dispatch thread must finish quickly; if they don't, unhandled events back up and the user interface becomes unresponsive."
To work around this, you'll want to run your long process in a background thread. You should have look at Concurrency in Swing - the section on Worker Threads and SwingWorker in particular.