I have a Manager class to which multiple threads register themselves (used UUID
to generate unique identifiers per requests), gives payload to process and get their corresponding responses from the manager. I am using java.util.concurrent.ExecutorService
to launch multiple threads. Here is an implementation for testing my Manager functionality-
public class ManagerTest {
public static void main(String[] args) {
try {
Manager myManager = new Manager();
// Start listening to the messages from different threads
myManager.consumeMessages();
int num_threads = Integer.parseInt(args[0]);
ExecutorService executor = Executors.newFixedThreadPool(num_threads);
for (int i = 0; i < num_threads; i++) {
// class implementation is given below
Runnable worker = new MyRunnable(myManager);
executor.execute(worker);
}
executor.shutdown();
// Wait until all threads are finish
while (!executor.isTerminated()) {
}
System.out.println("\nFinished all threads");
myManager.closeConnection();
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
}
}
Here is the implementation of MyRunnable
class
class MyRunnable implements Runnable {
private Manager managerObj;
public MyRunnable(Manager managerObj) {
this.managerObj = managerObj;
}
@Override
public void run() {
try {
Random rand = new Random();
int n = rand.nextInt(35);
String requestId = UUID.randomUUID().toString();
managerObj.registerRequest(requestId, n);
managerObj.publishMessage(requestId);
// Want to avoid this while loop
while( ! managerObj.getRequestStatus(requestId)){
}
int response = managerObj.getRequestResponse(requestId);
// do something else
managerObj.unregisterRequest(requestId);
} catch (IOException e) {
e.printStackTrace();
}
}
}
The manager will process requests and depending on the payload the response of a request can take varying amount of time. Whenever manager gets the response it sets the requests status to true by calling this function setRequestStatus(requestId)
. After this the thread will exit from the while loop
and continues its execution.
The code if working fine, but the thread is doing too much work then needed by continuously looping over the while loop until the condition is met.
Is their a way to make a thread sleep after sending the requests to the manager, and manager signals this thread to wake up when its response is ready.
Pardon me if this sounds too simple to someone, I am a newbie to java and java-threading interface.
What you are doing is a busy-spin-loop, and it is a very bad thing to do, because a) it consumes a full CPU core per thread, and b) it actually keeps the CPU busy, which means that it is stealing processing time from other threads that might have useful work to do.
There is a number of ways you can solve this, I will list them from worst to best.
The simplest way to improve your code is to invoke the java.lang.Thread.sleep(long millis)
method, passing it 0
as a parameter. This is also known as a "yield" operation, and it essentially means "if there are any other threads that have some useful work to do, let them run, and return back to me once they are done." This is only marginally better than busy-spin-looping because it will still consume 100% CPU. The benefit is that it will only consume CPU while other threads do not have anything to do, so at least it will not slow other things down.
The next best, but still not very smart, way to improve your code is by invoking the java.lang.Thread.sleep(long millis)
method passing it 1
as a parameter. This is known as a "pass" operation, and it essentially means "release the remainder of my time-slice to any other threads that might have some useful work to do". In other words, the remainder of the time-slice is forfeited, even if no useful work needs to be done in the entire system. This will bring CPU consumption down to almost zero. The disadvantages are that a) the CPU consumption will actually be slightly above zero, b) the machine will not be able to go to some low-power sleep mode, and c) your worker thread will be slightly less responsive: it will pick up work to do only on a timeslice boundary.
The best way to solve your problem is by using the synchronization mechanism built into java, as explained in this answer: https://stackoverflow.com/a/5999146/773113 This will not only consume zero CPU, but it will even allow the machine to go into a low power mode while waiting.
To solve your problem for the most general case, where you don't want to just wait until a condition, but to actually also pass information about the work that has been done or work which is to be done, you would use a BlockingQueue
. (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html) The blocking queue works using the java built-in synchronization mechanism and allows one thread to pass information to another.