private static ListmyObjects = new ArrayList ();
public static synchronized void putObject(String object) {
synchronized(myObjects) {
myObjects.add(object);
}
}
public static String getObject(int index) {
synchronized(myObjects) {
return myObjects.get(index);
}
}
The problem with this approach is that all the calls are serialized, meaning they will wait for the previous call to complete. To explain it more, say the update takes 100 milliseconds and each get takes 10 milliseconds each, you will see the following:
putObject() - started at 0 ms
getObject()[1] - called at 1 ms
getObject()[2] - called at 1 ms
getObject()[3] - called at 1 ms
putObject() - completed at 100 ms
getObject()[1] - completed at 110 ms
getObject()[2] - completed at 120 ms
getObject()[3] - completed at 130 ms
Now you might say that why don't you remove the synchronized block from the getObject method. Obviously you can but that will lead to reading inconsistent data. In this case the getObject calls will not wait for the updates to complete and get the current data from the list, which might be invalid or stale. If you business requirement is fine with this situation you can most welcome to remove the synchronized block from the getObject method.
But to make to that the getObject call do wait for the putObject call to finish, I use a wait-notify model. In this model all the thread making a getObject call will wait for the updateObject call to complete and then process with the getObject call without waiting for the other getObject calls to complete. To depict it in action, here is what you would see when you run the same test above:
putObject() - started at 0 ms
getObject()[1] - called at 1 ms
getObject()[2] - called at 1 ms
getObject()[3] - called at 1 ms
putObject() - completed at 100 ms
getObject()[1] - completed at 110 ms
getObject()[2] - completed at 110 ms
getObject()[3] - completed at 110 ms
You must have noticed that all the getObject calls happen at the same time at 110 ms and not 10 ms apart.
So how do I achieve this? Using a simple monitor object. This object has a simple boolen "isBusy" flag which is used to control whether a thread is allowed to proceed or not. Here is the code for the monitor implementation:
public class Monitor {
private boolean isBusy = false;
public synchronized void lock() {
// locks the monitor
while (true) {
if (isBusy) {
try {
wait();
} catch (InterruptedException e) {
// ignore
}
} else {
break;
}
}
isBusy = true;
}
public void checkLock() {
while (true) {
if (isBusy) {
synchronized(this) {
try {
wait();
} catch (InterruptedException e) {
// ignore
}
}
} else {
break;
}
}
}
public synchronized void releaseLock() {
isBusy = false;
this.notifyAll(); // this will release the lock
}
}
And this is the implementation of the putObject:
private static Monitor monitor = new Monitor();
public static synchronized void putObject(String object) {
monitor.lock(); // this will lock the monitor
try {
myObjects.add(object);
} finally {
monitor.releaseLock(); // this will release lock on the monitor
}
}
And this is the implementation of the getObject:
private static Monitor monitor = new Monitor();
public static String getObject(int index) {
monitor.checkLock(); // this will check for a lock on the monitor
return myObjects.get(index);
}
The notifyAll() call in the releaseLonc() methos will notify all thread waiting on the monitor that the lock has been released, which will allow all the thread to proceed with the execution at the same time.
No comments:
Post a Comment