Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Questions about Threads and Callbacks in Java

I am reading Network Programming in Java by Elliotte and in the chapter on Threads he gave this piece of code as an example of a computation that can be ran in a different thread

import java.io.*;
import java.security.*;
public class ReturnDigest extends Thread {
private String filename;
private byte[] digest;

public ReturnDigest(String filename) {
  this.filename = filename;
}

@Override
public void run() {
try {
   FileInputStream in = new FileInputStream(filename);
   MessageDigest sha = MessageDigest.getInstance("SHA-256");
   DigestInputStream din = new DigestInputStream(in, sha);
   while (din.read() != -1) ; // read entire file
   din.close();
   digest = sha.digest();
 } catch (IOException ex) {
   System.err.println(ex);
 } catch (NoSuchAlgorithmException ex) {
   System.err.println(ex);
 }
}

public byte[] getDigest() {
    return digest;
 }
}

To use this thread, he gave an approach which he referred to as the solution novices might use.

The solution most novices adopt is to make the getter method return a flag value (or perhaps throw an exception) until the result field is set.

And the solution he is referring to is:

public static void main(String[] args) {
 ReturnDigest[] digests = new ReturnDigest[args.length];
 for (int i = 0; i < args.length; i++) {
  // Calculate the digest
  digests[i] = new ReturnDigest(args[i]);
  digests[i].start();
 }
 for (int i = 0; i < args.length; i++) {
  while (true) {
   // Now print the result
   byte[] digest = digests[i].getDigest();
   if (digest != null) {
    StringBuilder result = new StringBuilder(args[i]);
    result.append(": ");
    result.append(DatatypeConverter.printHexBinary(digest));
    System.out.println(result);
    break;
   }
  }
 }
}

He then went on to propose a better approach using callbacks, which he described as:

In fact, there’s a much simpler, more efficient way to handle the problem. The infinite loop that repeatedly polls each ReturnDigest object to see whether it’s finished can be eliminated. The trick is that rather than having the main program repeatedly ask each ReturnDigest thread whether it’s finished (like a five-year-old repeatedly asking, “Are we there yet?” on a long car trip, and almost as annoying), you let the thread tell the main program when it’s finished. It does this by invoking a method in the main class that started it. This is called a callback because the thread calls its creator back when it’s done

And the code for the callback approach he gave is below:

import java.io.*;
import java.security.*;
public class CallbackDigest implements Runnable {
 private String filename;
 public CallbackDigest(String filename) {
  this.filename = filename;
 }
 @Override
 public void run() {
  try {
   FileInputStream in = new FileInputStream(filename);
   MessageDigest sha = MessageDigest.getInstance("SHA-256");
   DigestInputStream din = new DigestInputStream( in , sha);
   while (din.read() != -1); // read entire file
   din.close();
   byte[] digest = sha.digest();
   CallbackDigestUserInterface.receiveDigest(digest, filename); // this is the callback
  } catch (IOException ex) {
   System.err.println(ex);
  } catch (NoSuchAlgorithmException ex) {
   System.err.println(ex);
  }
 }
}

And the Implementation of CallbackDigestUserInterface and it's usage was given as:

public class CallbackDigestUserInterface {
 public static void receiveDigest(byte[] digest, String name) {
  StringBuilder result = new StringBuilder(name);
  result.append(": ");
  result.append(DatatypeConverter.printHexBinary(digest));
  System.out.println(result);
 }
 public static void main(String[] args) {
  for (String filename: args) {
   // Calculate the digest
   CallbackDigest cb = new CallbackDigest(filename);
   Thread t = new Thread(cb);
   t.start();
  }
 }
}

But my question (or clarification) is regarding what he said about this method...He mentioned

The trick is that rather than having the main program repeatedly ask each ReturnDigest thread whether it’s finished, you let the thread tell the main program when it’s finished

Looking at the code, the Thread that was created to run a separate computation is actually the one that continues executing the original program. It is not as if it passed the result back to the main thread. It seems it becomes the MAIN Thread!

So it is not as if the Main threads gets notified when the task is done (instead of the main thread polling). It is that the main thread does not care about the result. It runs to its end and it finishes. The new thread would just run another computation when it is done.

Do I understand this correctly?

How does this play with debugging? Does the thread now becomes the Main thread? and would the debugger now treat it as such?

Is there another means to actually pass the result back to the main thread?

I would appreciate any help, that helps in understanding this better :)

like image 634
Finlay Weber Avatar asked Nov 19 '19 19:11

Finlay Weber


People also ask

Can Java notify specific threads?

Java Thread notify() method This method gives the notification for only one thread which is waiting for a particular object. If we use notify() method and multiple threads are waiting for the notification then only one thread get the notification and the remaining thread have to wait for further notification.

Can we start a thread twice?

No. After starting a thread, it can never be started again. If you does so, an IllegalThreadStateException is thrown. In such case, thread will run once but for second time, it will throw exception.

Which method can make thread to go from running to waiting?

when sleep() is called on thread it goes from running to waiting state and can return to runnable state when sleep time is up.

Can Java threads communicate with each other?

Because Java threads run in the same memory space, they can easily communicate among themselves because an object in one thread can call a method in another thread without any overhead from the operating system.


3 Answers

It is a common misunderstanding to think that the "main" thread, the one that public static void main is run on, should be considered the main thread for the application. If you write a gui app for instance, the starting thread will likely finish and die well before the program ends.

Also, callbacks are normally called by the thread that they are handed off to. This in true in Swing, and in many other places (including DataFetcher, for example)

like image 94
ControlAltDel Avatar answered Oct 06 '22 18:10

ControlAltDel


None of the other threads become the "main thread". Your main thread is the thread that starts with the main() method. It's job is to start the other threads... then it dies.

At this point, you never return to the main thread, but the child threads have callbacks... and that means that when they are done, they know where to redirect the flow of the program.

That is your receiveDigest() method. Its job is to display the results of the child threads once they complete. Is this method being called from the main thread, or the child threads? What do you think?

It is possible to pass the result back to the main thread. To do this, you need to keep the main thread from terminating, so it will need to have a loop to keep it going indefinitely, and to keep that loop from eating up processor duty, it will need to be put to sleep while the other threads work.

You can read an example of fork and join architecture here:

https://www.tutorialspoint.com/java_concurrency/concurrency_fork_join.htm

like image 1
LowKeyEnergy Avatar answered Oct 06 '22 20:10

LowKeyEnergy


The book is misleading you.

First of all, there is no Callback in the example. There is only one function calling another function by name. A true callback is a means for communication between different software modules. It is pointer or reference to a function or object-with-methods that module A provides to module B so that module B can call it when something interesting happens. It has nothing at all to do with threads.

Second of all, the alleged callback communicates nothing between threads. The function call happens entirely in the new thread, after the main() thread has already died.

like image 1
Solomon Slow Avatar answered Oct 06 '22 20:10

Solomon Slow