Do I use a SwingWorker? - java

While my code sends http calls I want it to alter the JLabel on a window (basically showing approximately how much time is left before the http calls end). I have been looking into SwingWorkers due to another question I asked here, but I'm not sure how I use it. The code I am writing basically has a loop to send the calls, each time timing how long it takes to run the call, calculates the approximate time left and then sends this to the JLabel (NB the JLabel is in a different instantiated object).
Most SwingWorker examples show a function continuing in the background that is not affected by the worker thread (e.g. a counter based entirely on time rather than being altered by the code). If this is the case then isn't the alteration of the JLabel just part of the worker thread as it the code runs through new loop -> calculate time & make call -> alter JLabel? I'm probably wrong but then how do I have the JLabel altered by the code rather than a independent thread?
One of my issues was that when I initially set my code up there was nothing changing in the JLabel.
Here is my code:
package transcription.windows;
import javax.swing.*;
import java.awt.*;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class PleaseWaitWindow {
private JLabel pleaseWaitJLabel = new JLabel("Please wait");
private GridBagConstraints containerGbc = new GridBagConstraints();
private Container contentPaneContainer = new Container();
private JFrame pleaseWaitJFrame;
public JLabel getPleaseWaitJLabel() {
return pleaseWaitJLabel;
}
public JFrame setPleaseWaitWindow() {
pleaseWaitJFrame = new JFrame();
contentPaneContainer = setContentPane();
pleaseWaitJFrame.setContentPane(contentPaneContainer);
pleaseWaitJFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
pleaseWaitJFrame.setTitle("");
pleaseWaitJFrame.setSize(350, 150);
pleaseWaitJFrame.setLocationRelativeTo(null);
pleaseWaitJFrame.setVisible(true);
return pleaseWaitJFrame;
}
private Container setContentPane() {
containerGbc.insets.bottom = 1;
containerGbc.insets.top = 2;
containerGbc.insets.right = 1;
containerGbc.insets.left = 1;
containerGbc.weightx = 1;
containerGbc.weighty = 1;
contentPaneContainer.setLayout(new GridBagLayout());
contentPaneContainer.setSize(800, 700);
setPleaseWaitJLabel();
return contentPaneContainer;
}
private void setPleaseWaitJLabel() {
containerGbc.gridx = 2;
containerGbc.gridy = 5;
containerGbc.gridwidth = 2;
containerGbc.gridheight = 1;
contentPaneContainer.add(pleaseWaitJLabel, containerGbc);
}
public void setJLabelDisplay(String displayTime) {
pleaseWaitJLabel.setText(displayTime);
}
public void closeWindow() {
pleaseWaitJFrame.dispose();
}
}
Method that is part of the ServiceUpload class:
public String cuttingLoop(String mpBase64Piece, String jobName, String email) {
Integer numberOfPiecesMinusEnd = (int) Math.ceil(mpBase64Piece.length() / 500000.0);
List<String> base64List = new ArrayList<>();
for (int i = 0; i < numberOfPiecesMinusEnd; i++) {
if (mpBase64Piece.length() >= 500000) {
base64List.add(mpBase64Piece.substring(0, 500000));
mpBase64Piece = mpBase64Piece.substring(500000);
}
}
base64List.add(mpBase64Piece);
pleaseWaitWindow = new PleaseWaitWindow();
pleaseWaitWindow.setPleaseWaitWindow();
for (int n = 0; n < base64List.size(); n++) {
numberOfLoopsLeft = numberOfPiecesMinusEnd - n;
Stopwatch stopwatch = null;
String tag;
Stopwatch.createStarted();
if (base64List.get(n) != null) {
if (n == 0) {
tag = "start";
} else if (n == base64List.size() - 1) {
tag = "end";
} else {
tag = "middle";
}
stopwatch = Stopwatch.createStarted();
response = providerUpload.executeUploadHttp(base64List.get(n), jobName, tag, email);
stopwatch.stop();
}
long oneLoopTime = stopwatch.elapsed(TimeUnit.SECONDS);
pleaseWaitWindow.setJLabelDisplay(numberOfLoopsLeft*oneLoopTime+" seconds remaining");
LOGGER.info("complete");
}
pleaseWaitWindow.closeWindow();
return response;
}
One of my issues was the code did not show the 'JLabel' when a 'SwingWorker' isn't used with the above code.

It's best you split up your code into areas of responsibilities. Let's go with three: 1. the worker (ie the upload); 2. the display (ie the JLabel update); 3. integration of the two (the first two are independent of each other, so you'll need something to tie them together).
Abstracting from the actual work, you can use standard interfaces. The first one is just a Runnable, ie not taking any parameters and not returning anything. The second one is a Consumer<String> because it takes a String (to display) but doesn't return anything. The third will be your main control.
Let's start with the control because that's simple:
Consumer<String> display = createDisplay();
Runnable worker = createWorker();
CompletableFuture.runAsync(worker);
This will start the worker in a separate Thread which is what it sounds like you want.
So here's your uploader:
Consumer<String> display = // tbd, see below
Runnable worker = () -> {
String[] progress = {"start", "middle", "finish"};
for (String pr : progress) {
display.accept(pr);
Thread.sleep(1000); // insert your code here
}
}
Note that this worker actually does depend on the consumer; that is somewhat "unclean", but will do.
Now for the display. Having defined it as a Consumer<String>, it's abstract enough that we can just print the progress on the console.
Consumer<String> display = s -> System.out.printf("upload status: %s%n", s);
You however want to update a JLabel; so the consumer would look like
Consumer<String> display = s -> label.setText(s);
// for your code
s -> pleaseWaitWindow.getPleaseWaitLabel().setText(s);
Your actual question
So if you do that, you will notice that your label text doesn't get updated as you expect. That is because the label.setText(s) gets executed in the thread in which the worker is running; it needs to be inserted in the Swing thread. That's where the SwingWorker comes in.
The SwingWorker has a progress field which is what you can use for your labels; it also has a doInBackground() which is your actual upload worker thread. So you end up with
class UploadSwingWorker {
public void doInBackground() {
for(int i = 0; i < 3; i++) {
setProgress(i);
Thread.sleep(1000); // again, your upload code
}
}
}
So how does that update your label? The setProgress raises a PropertyChangeEvent you can intercept; this done using a PropertyChangeListener with the signature
void propertyChange(PropertyChangeEvent e)
This is a functional interface, so you can implement this with a lambda, in your case
String[] displays = {"start", "middle", "finish"};
updateLabelListener = e -> {
int index = ((Integer) e.getNewValue()).intValue(); // the progress you set
String value = displays[index];
label.setText(value);
}
and you can add it to the SwingWorker using
SwingWorker uploadWorker = new UploadSwingWorker();
uploadWorker.addPropertyChangeListener(updateLabelListener);
uploadWorker.execute(); // actually start the worker
Simpler
Note that I myself have never used a SwingWorker this way. The much simpler way to get around the problem that the GUI is not updated from within your worker thread is to call the GUI update using SwingUtilities.invokeLater().
Coming back to the initial Consumer<String> I brought up, you can do
Consumer<String> display = s -> SwingUtilities.invokeLater(
() -> pleaseWaitWindow.getPleaseWaitLabel().setText(s)
);
and that should do. This allows you to keep your worker in the more abstract Runnable and use the usual scheduling mechanisms to run it (ExecutorService.submit() or CompletableFuture.runAsync() for example), while still allowing to update the GUI on a similarly simple level.

Related

Moving and organizing Java codes on another files

I would like to create a new file and put this block of action buttons codes to be organized on the other files i dont know how i would be able to move it.
Iam making this code and its a little bit disorganized and im having a hard time on what is their purpose.
Is there any other easier method to create a actionlistener codes?
public void buttonAction (){
bgButton[0].addActionListener(e -> {
bgPanel[0].setVisible(false);
bgPanel[0].remove(bgButton[0]);
bgPanel[1].setVisible(true);
});
for (int a = 3,c=0 ; a <12; a++, c++){
final int b=c;
final int d=a;
bgButton[a].addActionListener(e -> {
if (input>=0&&input <=9&&Num[b]!=0){
input = Num[b]*10;
if(input!=0)
createObject(1,12,283,245,85,61,numFile[b]);//12
bgButton[d].setEnabled(false);
Number[b]=-1;
} else if (input >9&&input<100&&input%10==0&&Num[b]!=0&&buttonClicked){
input += Num[b];
buttonClicked = !buttonClicked;
createObject(1,13,432,245,85,61,numFile[b]);//13
bgButton[d].setEnabled(false);
Number[b]=-1;
}else if (Num[b]==0&&buttonClicked){
input += Num[b];
createObject(1,13,432,245,85,61,numFile[b]);//13
bgButton[d].setEnabled(false);
buttonClicked = !buttonClicked;
Number[b]=-1;
}
System.err.println("total " + input);
bgPanel[1].revalidate();
bgPanel[1].repaint();
});
}
bgButton[14].addActionListener(e -> {
for (int c =0 ,a=3; c <9; a++,c++){
final int b = a;
final int d=c;
int firstNum = input/10;
int secondNum=input%10;
if (Number[c]==-1&&bgButton[13]!=null){
attack = input;
generateSoloNum(d);
bgButton[b].setEnabled(true);
updateButtonIcon(b,64,48,numFile[d]);
bgPanel[1].remove(bgButton[12]);
bgPanel[1].remove(bgButton[13]);
bgPanel[1].revalidate();
bgPanel[1].repaint();
Number [c]=0;
input =0;
}
}
buttonClicked = true;
bgButton[13]=null;
});
createObject(1,15,149,244,50,38,"res/imageAssets/x.png");//15
bgButton[15].addActionListener(e -> {
input = 0;
bgPanel[1].remove(bgButton[12]);
bgPanel[1].remove(bgButton[13]);
bgPanel[1].revalidate();
bgPanel[1].repaint();
buttonClicked = true;
for (int a = 3,c=0; a<12;a++,c++){
bgButton[a].setEnabled(true);
Number [c]=0;
}
});
}
I tried import Main.UI; and import Main.Action;
Action is the new file that i want to move it into.
tried using chat gpt but it makes no sense
All methods in Java need to be associated with a class. You cannot move methods from listeners into separate files.
You can create separate listener implementations that include these action methods. If you refactor that way you'll give instances of those listener implementations to your Swing frame and call them instead of keeping them in one big Swing class.
I think this is a good idea. You would change implementations by injecting new classes instead of modifying the frame code.
Each of those addActionListener lambdas would become part of separate classes.
Swing UI code tends to turn into walls of code if you're not careful. I have never seen a well decomposed Swing UI application posted here.

how can I get array values back from a thread

I have a thread, in Java, putting the first 100 Fibonacci numbers in an array. How do I get the numbers back from the thread. Is there an interrupt, handling, exception, implements, extends? I have been adding things and the trial and error is not getting me anywhere to understanding.
import java.util.Scanner;
import java.io.*;
import java.lang.Thread; //don't know if this is needed
public class FibThread extends Thread{
public FibThread (){
super();
}
public void run(int inputNum){
System.out.println(inputNum);
long[] fibArray = new long[inputNum];
fibArray[0]=0;
fibArray[1]=1;
fibArray[2]=1;
for(int i = 3; i<inputNum; i++){
fibArray[i]= fibArray[i-1] + fibArray[i-2];
// }
//System.out.println( );
// for(int j = 0; j<= inputNum; j++){
int output = (int) fibArray[i];
System.out.println(output);
}
}
public static void main(String[] args){
Scanner keyboard = new Scanner(System.in);
FibThread threadOne;
int inputNum, itter, output;
System.out.println("Please enter the number of Fibonacci numbers to be generated: ");
itter = keyboard.nextInt();
//inputNum = itter;
threadOne = new FibThread();
threadOne.start();
// for(int j = 0; j<= inputNum; j++){
// int output = (int) fibArray[j];
// System.out.println(output);
}
}
If you have a "task" that returns a value, make it a Callable.
If you want the callable to run in a background thread, then instead of handling the creation and execution of threads yourself, it's generally better to abstract this through an ExecutorService. A caller can interact with the service by passing in a Callable, and getting back a Future that will be populated with the value when the calculation has completed.
To modify your example, renaming FibThread to FibCalc:
public class FibCalc implements Callable<Integer> {
// We need some way to pass in the initial input - must be through the
// constructor and we'll store it here
private final inputNum;
public FibCalc(int inputNum) {
this.inputNum = inputNum;
}
public int call() {
// The same as your run() method from before, except at the end:
...
return output;
}
}
// And now for your main() method
public static void main(String[] args) throws Exception {
// As before up to:
...
itter = keyboard.nextInt();
// Create a very simple executor that just runs everything in a single separate thread
ExecutorService exec = Executors.newSingleThreadExecutor();
// Create the calculation to be run (passing the input through the constructor)
FibCalc calc = new FibCalc(itter);
// Send this to the executor service, which will start running it in a background thread
// while giving us back the Future that will hold the result
Future<Integer> fibResult = exec.submit(fibCalc);
// Get the result - this will block until it's available
int result = fibResult.get();
// Now we can do whatever we want with the result
System.out.println("We got: " + result);
}
If you absolutely have to create a Thread object yourself (due to artificial constraints on a homework question, or something like that - I can't see why one would realistically do this in reality), then the approach has to be different. You can't return a value because run() must return void due to the interface. So my approach here would be to store the result in a local variable of the FibThread class, and then add a method to that class (e.g. public int getResult()) which returned that variable.
(If you're doing it this way, bear in mind that you'll have to handle the concurrency issues (i.e. letting the caller know the result is ready) yourself. A naive approach, where the main method starts the thread and then immediately calls getResult(), means that it will almost certainly get an "empty" result before the calculation has finished. A crude solution to this problem would be calling join() on the spawned thread, to wait for it to finish before accessing the result.)

Why does a concurrent hash map work properly when accessed by two thread, one using the clear() and other using the putifAbsent() methods?

I am implementing an application using concurrent hash maps. It is required that one thread adds data into the CHM, while there is another thread that copies the values currently in the CHM and erases it using the clear() method. When I run it, after the clear() method is executed, the CHM always remains empty, though the other thread continues adding data to CHM.
Could someone tell me why it is so and help me find the solution.
This is the method that adds data to the CHM. This method is called from within a thread.
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
public static ConcurrentMap<String, String> updateJobList = new ConcurrentHashMap<String, String>(8, 0.9f, 6);
public void setUpdateQuery(String ticker, String query)
throws RemoteException {
dataBaseText = "streamming";
int n = 0;
try {
updateJobList.putIfAbsent(ticker, query);
}
catch(Exception e)
{e.printStackTrace();}
........................
}
Another thread calls the track_allocation method every minute.
public void track_allocation()
{
class Track_Thread implements Runnable {
String[] track;
Track_Thread(String[] s)
{
track = s;
}
public void run()
{
}
public void run(String[] s)
{
MonitoringForm.txtInforamtion.append(Thread.currentThread()+"has started runnning");
String query = "";
track = getMaxBenefit(track);
track = quickSort(track, 0, track.length-1);
for(int x=0;x<track.length;x++)
{
query = track[x].split(",")[0];
try
{
DatabaseConnection.insertQuery(query);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
joblist = updateJobList.values();
MonitoringForm.txtInforamtion.append("\nSize of the joblist is:"+joblist.size());
int n = joblist.size()/6;
String[][] jobs = new String[6][n+6];
MonitoringForm.txtInforamtion.append("number of threads:"+n);
int i = 0;
if(n>0)
{
MonitoringForm.txtInforamtion.append("\nSize of the joblist is:"+joblist.size());
synchronized(this)
{
updateJobList.clear();
}
Thread[] threads = new Thread[6];
Iterator it = joblist.iterator();
int k = 0;
for(int j=0;j<6; j++)
{
for(k = 0; k<n; k++)
{
jobs[j][k] = it.next().toString();
MonitoringForm.txtInforamtion.append("\n\ninserted into queue:\n"+jobs[j][k]+"\n");
}
if(it.hasNext() && j == 5)
{
while(it.hasNext())
{
jobs[j][++k] = it.next().toString();
}
}
threads[j] = new Thread(new Track_Thread(jobs[j]));
threads[j].start();
}
}
}
I can see a glaring mistake. This is the implementation of your Track_Thread classes run method.
public void run()
{
}
So, when you do this:
threads[j] = new Thread(new Track_Thread(jobs[j]));
threads[j].start();
..... the thread starts, and then immediately ends, having done absolutely nothing. Your run(String[]) method is never called!
In addition, your approach of iterating the map and then clearing it while other threads are simultaneously adding is likely to lead to entries occasionally being removed from the map without the iteration actually seeing them.
While I have your attention, you have a lot of style errors in your code:
The indentation is a mess.
You have named your class incorrectly: it is NOT a thread, and that identifier ignores the Java identifier rule.
Your use of white-space in statements is inconsistent.
These things make your code hard to read ... and to be frank, they put me off trying to really understand it.

JTextFiled not updating when edited in a while loop

I want the JTextFiled to go up quickly as if it were a clock
the following code is inside a mouse listener.
Also t1 IS public and was declared before the main method in the class.
If you any questions pleas tell me!
ddd = 1. The rest of the switch statement is the same just instead of t1 its t2 or t3 or t4...
try {
String rol = null;
String har = null;
int rolx = 0;
int harx = 0;
int newl = 0;
switch (ddd) {
case 1:
rol = t1.getText();
har = numb.getText();
rolx = Integer.parseInt(rol);
harx = Integer.parseInt(har);
newl = rolx + harx;
while (harx > 0) {
harx --;
rolx ++;
Thread.sleep(10);
System.out.println(""+rolx);
t1.setText(""+rolx);
}
t1.setText(""+newl);
break;
Anything you do directly inside a Listener's event handling code holds the event handling thread, preventing any GUI activity.
You need to either move the loop to another thread, or use some form of timer instead.

Create a simple queue with Java threads

I'm trying to create a simple queue with Java Thread that would allow a loop, say a for loop with 10 iterations, to iterate n (< 10) threads at a time and wait until those threads are finished before continuing to iterate.
Here's a better way to illustrate my problem:
for (int i = 1; i <= 10; i++) {
new Thread ( do_some_work() );
if ( no_available_threads ) {
wait_until_available_threads();
}
}
do_some_work() {
// do something that takes a long time
}
Basically what I want to do is a copy of this: Thread and Queue
How can I achieve this the most painless way?
I would use the Java 5 Executors instead of rolling your own. Something like the following:
ExecutorService service = Executors.newFixedThreadPool(10);
// now submit our jobs
service.submit(new Runnable() {
public void run() {
do_some_work();
}
});
// you can submit any number of jobs and the 10 threads will work on them
// in order
...
// when no more to submit, call shutdown, submitted jobs will continue to run
service.shutdown();
// now wait for the jobs to finish
service.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
Use the Executors, as recommended by the others. However, if you want the fun of doing it yourself, try something like this. (Take care. I wrote it in Notepad and there's some Exceptions you'll need to catch even if I got everything else right. Notepad's poor at catching coding errors.) This is more a concept than an actual solution to anything, but the idea could be generally useful.
private ConcurrentLinkedQueue<MyThread> tQueue =
new ConcurrentLinkedQueue<MyThread>();
class MyThread extends Thread {
public Runnable doSomething;
public void run() {
// Do the real work.
doSomething();
// Clean up and make MyThread available again.
tQueue.add( mythread );
// Might be able to avoid this synch with clever code.
// (Don't synch if you know no one's waiting.)
// (But do that later. Much later.)
synchronized (tQueue) {
// Tell them the queue is no longer empty.
tQueue.notifyAll();
}
}
}
Elsewhere:
// Put ten MyThreads in tQueue.
for (int i = 0; i < 10; i++) tQueue.add( new MyThread() );
// Main Loop. Runs ten threads endlessly.
for (;;) {
MyThread t = tQueue.poll();
if (t == null) {
// Queue empty. Sleep till someone tells us it's not.
do {
// There's a try-catch combo missing here.
synchonized( tQueue ) { tQueue.wait() };
t = tQueue.poll();
} while (t == null) break; // Watch for fake alert!
}
t.doSomething = do_some_work;
t.start();
}
Also, note the clever use of ConcurrentLinkedQueue. You could use something else like ArrayList or LinkedList, but you'd need to synchronize them.
see java.util.concurrent and especially Executors and ExecutorService
Crate Logger.class :
public class Logger extends Thread {
List<String> queue = new ArrayList<String>();
private final int MAX_QUEUE_SIZE = 20;
private final int MAX_THREAD_COUNT = 10;
#Override
public void start() {
super.start();
Runnable task = new Runnable() {
#Override
public void run() {
while (true) {
String message = pullMessage();
Log.d(Thread.currentThread().getName(), message);
// Do another processing
}
}
};
// Create a Group of Threads for processing
for (int i = 0; i < MAX_THREAD_COUNT; i++) {
new Thread(task).start();
}
}
// Pulls a message from the queue
// Only returns when a new message is retrieves
// from the queue.
private synchronized String pullMessage() {
while (queue.isEmpty()) {
try {
wait();
} catch (InterruptedException e) {
}
}
return queue.remove(0);
}
// Push a new message to the tail of the queue if
// the queue has available positions
public synchronized void pushMessage(String logMsg) {
if (queue.size() < MAX_QUEUE_SIZE) {
queue.add(logMsg);
notifyAll();
}
}
}
Then insert bellow code in your main class :
Logger logger =new Logger();
logger.start();
for ( int i=0; i< 10 ; i++) {
logger.pushMessage(" DATE : "+"Log Message #"+i);
}

Categories