At times we often need to fetch an object which might take a long time. Our preferred way of doing that, especially when we are on a UI thread, is to spawn a different thread so as to keep the UI responsive (this is just one of the many use cases that I can think of now). But since we need that object to proceed further in the current execution, we have to resort to some sort of wait/notify mechanism. The following code demoes a very simplistic approach using the regular wait()/notify().
package com.swayam.thread.test;
/**
*
* @author paawak
*
*/
public class WaitNotifyExample {
private Object lock = new Object();
public WaitNotifyExample() {
}
public void runLongTask() {
Thread job = new Thread(new Runnable() {
public void run() {
System.out.println("Long task started");
try {
int maxCount = 1000;
// int maxCount = Integer.MAX_VALUE;
// do some long task
for (int i = 0; i < maxCount; i++) {
System.out.println(i);
}
System.out.println("Long task done.");
} finally {
System.out.println("About to notify lock...");
synchronized (lock) {
lock.notifyAll();
}
System.out.println("Lock notified");
}
}
});
// job.setPriority(Thread.MAX_PRIORITY - 2);
System.out.println("Starting a long task...");
job.start();
System.out
.println("Pausing normal execution and waiting for long task to finish...");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Resuming normal execution as long task is done.");
}
public static void main(String[] a) {
new WaitNotifyExample().runLongTask();
}
}
Note: This is far from fool proof. One case where it will fail is if the long task is over before that lock.wait() is called.
I have tried to come-up with a thread safe and a non thread safe version of a typical producer/consumer scenario.
package com.swayam.exp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
/**
*
* @author paawak
*/
public class ProducerConsumerSimulator implements Runnable {
private final int sleepInterval;
private final List<String> list;
private final boolean producer;
private final boolean threadSafe;
public ProducerConsumerSimulator(boolean producer, boolean threadSafe,
int sleepInterval, List<String> list) {
this.producer = producer;
this.sleepInterval = sleepInterval;
this.list = list;
this.threadSafe = threadSafe;
}
public void run() {
while (true) {
try {
Thread.sleep(sleepInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (producer) {
produce();
if (threadSafe) {
synchronized (list) {
System.out.println("Consumer, wake-up!!!");
list.notify();
}
}
} else {
if (threadSafe) {
synchronized (list) {
try {
System.out.println("Waiting for producer...");
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
consume();
}
}
}
private void produce() {
Calendar cal = Calendar.getInstance();
String str = "Time Now -- " + cal.get(Calendar.MINUTE) + ":"
+ cal.get(Calendar.SECOND) + ":"
+ cal.get(Calendar.MILLISECOND);
list.add(str);
System.out.println("ADDED:: " + str);
}
private void consume() {
try {
String str = list.remove(0);
System.out.println("REMOVED:: " + str);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
/**
* For testing
*
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
int PRODUCER_SLEEP_INTERVAL = 1000;
int CONSUMER_SLEEP_INTERVAL = 30;
boolean THREAD_SAFE_EXECUTION = true;
List<String> list = new ArrayList<String>();
Thread producer = new Thread(new ProducerConsumerSimulator(true,
THREAD_SAFE_EXECUTION, PRODUCER_SLEEP_INTERVAL, list));
producer.start();
Thread consumer = new Thread(new ProducerConsumerSimulator(false,
THREAD_SAFE_EXECUTION, CONSUMER_SLEEP_INTERVAL, list));
consumer.start();
}
}
Run this with
boolean THREAD_SAFE_EXECUTION = true;
and
boolean THREAD_SAFE_EXECUTION = false;
And see the difference yourself :).