I’m developing a demo program that shows the impact of different ways of handling threads. I’m using a simple task of taking a char array and building a name sending each char to a StringBuilder which calls a runnable called NameBuilder which in turn uses a letterAdder class with synchronized methods and a static StringBuilder variable to hold the name as it is built. This structure is loosely based on the WaxoMatic example in “Thinking in Java” by Bruce Eckel
Here is the code that I am using for the notifyAll() method:
import java.util.List;
import java.util.concurrent.*;
class LetterAdder{
public static StringBuilder currName = new StringBuilder();
public static boolean busyAdding = false;
public synchronized void addLetterSynchWithNotifyAll(char letter) throws InterruptedException{
while (busyAdding){
System.out.println("Letter " +letter+" has to wait");
wait();
}
busyAdding=true;
System.out.println("About to add " + letter + " - ");
currName.append(letter);
System.out.println("name is now " + currName);
busyAdding=false;
}
public synchronized void doNotifyAll(char letter) throws InterruptedException{
System.out.print(letter + " is notifying all");
notifyAll();
}
}
class NameBuilder implements Runnable{
private LetterAdder adder = new LetterAdder();
private char nextLetter;
public NameBuilder(char nextLetter){
adder = new LetterAdder();
this.nextLetter=nextLetter;
}
public void run(){
try{
adder.addLetterSynchWithNotifyAll(nextLetter);
adder.doNotifyAll(nextLetter);
} catch (InterruptedException e) {
System.out.println("Letter "+nextLetter+" interrupted!");
}
//tie up thread for specified time to ensure other threads go to wait
try{
TimeUnit.SECONDS.sleep(1);
} catch(Exception e){}
}
}
public class NotifyDemo {
public static void main(String[] args) {
char[] chars = {'M', 'a','r','t','i','n'};
ExecutorService pool = Executors.newCachedThreadPool();
for (int i=0; i<chars.length; i++){
NameBuilder builder = new NameBuilder(chars[i]);
pool.execute(builder);
}
System.out.println("\nWill shutdown thread pool in 20 seconds");
try {
TimeUnit.SECONDS.sleep(20);
} catch (InterruptedException e) {}
System.out.println("\nAbout to shutdown thread pool ");
List<Runnable> tasks = pool.shutdownNow();
if (!tasks.isEmpty()){
for (Runnable r: tasks){
System.out.println("Uncompleted task: "+r.toString());
}
}
}
}
/** Output
About to add M -
Letter a has to wait
name is now M
Letter r has to wait
M is notifying allLetter t has to wait
About to add i -
Will shutdown thread pool in 20 seconds
Letter n has to wait
name is now Mi
i is notifying all
About to shutdown thread pool
Letter a interrupted!
Letter t interrupted!
Letter n interrupted!
Letter r interrupted!
*/
As can be seen from the printed statements in the output, letters get blocked when they reach the busyAdding monitor and print the waiting message but they never come out of the waiting state even when a notifAlly() is sent.
Also, I would expect the interrupted threads to appear in the List returned by the shutdownNow() statement but the list is empty.
I have a feeling i am missing something obvious, can anyone spot what I am doing wrong here?
You are sharing the busyAdding between your different adders, but lock and notify on each different adder.
This means that the following can happen. Say you create three adders, ‘O’, ‘p’ and ‘s’, the first might enter addLetterSynchWithNotifyAll, pass by the guard and set the busy signal to true. Now, the next two runners enter, and get stuck at the guard. When the first threads exist it notifies all threads waiting at the first runner (none at the moment). You want to share the signal between the objects.
You can either make the addLetterSynchWithNotifyAll static which means the synchronised will be on class level, and then you need to change your wait to be on the class, not instance. Or you can create a common lock object used for synchronization and waiting.
If you make the method static, notice that no other thread will be entering the method since they are now synchronized on the same object, and your guarding with busyAdding is unnecessary.