In this series we will discuss different concurrent structures provided in the new Java 5 Concurrent package.
We will start with a simple parallel processing concept called "barrier" that we have all studied and forgotten. By definition if there are set of independent tasks (T1..Tn) that need to fully completed to initiate another task (Tx) which is dependent on all these tasks (T1...Tn) we can synchronize them using a barrier.
Each of these independent tasks (T1...Tn) will indicate they have finished processing using an indicator in the barrier. The dependent Task Tx, will wait on the barrier for a signal to indicate that all dependent tasks are done. Barrier keeps track of all independent tasks and notifies Tx when all of them are done or if one or more of these tasks will never be done ( reset).
Let's look at an example, we have a compilation unit of "c" code consisting of 10 .c files. Compiler will first convert all .c files to .o files ( object code). If all files have been compiled successfully it will link them to create an executable. ( wasn't that a jog down your memory lane...). An efficient compile will analyze the dependency and compile independent files in parallel.
For our discussion, let's assume all compilation files are independent of each other. Given below is a snippet of the compile class which represents an independent task (T1...Tn). When each task is successfully completed, it notifies the barrier that the task is done by calling barrier.await()
- public class Compiler implements Runnable {
- private String fileName;
- private CyclicBarrier barrier;
- boolean compileErr=false;
- public Compiler(String fileName, CyclicBarrier barrier){
- this.fileName=fileName;
- this.barrier=barrier; //each task should know the barrier its working with
- }
- @Override
- public void run() {
- if ( compile())
- {
- barrier.reset(); //if compilation failed, notify the barrier.
- }
- try {
- barrier.await(); //notify the barrier task done. It will increment the count of tasks done.
- System.out.println("released from the barrier");
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (BrokenBarrierException e) {
- e.printStackTrace();
- }
- }
- /* returns false if compilation fails */
- private boolean compile(){
- System.out.println("Compiling "+ fileName);
- return false;
- }
- }
Now we have the dependent task Tx defined as the Linker. This task should be executed only if all the tasks (T1...Tn) completes successfully. If one of the tasks fail, Tx should never be executed. Tx is defined in the class Linker. It's a simple class that implements Runnable.
- public class Linker implements Runnable
- {
- public void run() {
- System.out.println("Done linking- releasing the barrier");
- }
- }
The key class is the CreateExecutable class that coordinates the tasks using a CyclicBarrier from Java Concurrent Util package. This creates the CyclicBarrier class with the number of independent tasks i.e files to be compiled and the dependent task (Tx) which is the Linker. In the next step, all the independent tasks are spawned by starting the threads. Each Compiler object executes the compile method and if successful will wait at the barrier. When all independent tasks are done, the barrier executes the linker task and releases the compiler tasks from the barrier.
- public class CreateExecutable {
- private int filesToCompile=10;
- private CyclicBarrier barrier;
- private ArrayList fileList=new ArrayList();
- public CreateExecutable() {
- Linker linker=new Linker();
- barrier=new CyclicBarrier(filesToCompile,linker);
- //create Runnable Compilers with a common barrier
- for(int i=0; i
- fileList.add(new Compiler("file_"+i,barrier));
- }
- //Start all compiler threads
- for(int i=0; i
- new Thread((Runnable)fileList.get(i)).start();
- }
- }
- public static void main(String[] args)
- {
- CreateExecutable example =new CreateExecutable();
- }
- }













Post new comment