October 24, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Simple Thread Control With Java's CountDownLatch

  • November 27, 2007
  • By Jeff Langr
  • Send Email »
  • More Articles »

Listing 2: LineCounter.

import java.io.*;

public class LineCounter implements Runnable {
   static final int NOT_CALCULATED = -1;
   private final BufferedReader reader;
   private int count = NOT_CALCULATED;
   private String filename;

   public LineCounter(BufferedReader reader) {
      this.reader = reader;
   }

   public LineCounter(File file) throws FileNotFoundException {
      this(new BufferedReader(new FileReader(file)));
      this.filename = file.getName();
   }

   public String getFilename() {
      return filename;
   }

   public void run() {
      count = 0;
      try {
         while (reader.readLine() != null)
            count++;
      } catch (IOException e) {
         count = NOT_CALCULATED;
      }
   }

   public int getCount() {
      return count;
   }
}

To demonstrate the CountDownLatch class, I implemented a simple server that processes a number of LineCounter requests in a threaded manner. The definition I chose for the server's interface is simple: It accepts a queue of requests, after which the server can be started.

The CountDownLatch is initialized with a number that represents how many times the latch can be counted down. In a typical use, an application creates a CountDownLatch initialized to a number x, and then creates x threads. The application immediately blocks by calling the await method on the CountDownLatch. When each of the spawned threads completes, it tells the latch to count down. When the latch count reaches zero, any code blocking on await now restarts.

The CountDownLatch can only count down, not up. As such, it's most applicable when the number of threads to create is known beforehand. A different design of the LineCountServer would process individual incoming requests as they were received; this would no longer represent a design for which CountDownLatch was applicable.

The test, shown in Listing 3, demonstrates the protocol for client applications interacting with a LineCountServer.

Listing 3: LineCountServerTest.

import static org.junit.Assert.*;
import org.junit.*;
import java.io.*;
import java.util.*;

public class LineCountServerTest {
   @Test
   public void multipleRequests() {
      LineCountServer server = new LineCountServer();
      LineCounter counter1 = new LineCounter(new BufferedReader(
            new StringReader("a")));
      LineCounter counter2 = new LineCounter(new BufferedReader(
            new StringReader("a\nb")));
      Queue<LineCounter> queue = new LinkedList<LineCounter>();
      queue.add(counter1);
      queue.add(counter2);
      server.setRequests(queue);
      server.run();
      assertEquals(1, counter1.getCount());
      assertEquals(2, counter2.getCount());
   }
}

The LineCountServer code is shown in Listing 4. The waitOnLatch method encapsulates a loop that handles any spurious wakeups from the await function.

The run method in the server controls processing of the counting thread. First, the latch field is initialized to a CountDownLatch with a count that matches the queue size. The while loop iterates until the queue is empty; an element is removed and processed with each iteration of the loop. For each LineCounter object removed from the queue, a new processing thread is created and started. This allows the loop to quickly iterate through the queue and initiate threads for each LineCounter.

Once the queue is emptied and all LineCounter execution threads have been spawned, the run method blocks by calling a method whose job is to wait on the CountDownLatch. As each spawned LineCounter thread completes, it triggers a countdown on the latch. Once the countdown reaches zero, the call to await unblocks.

Listing 4: LineCountServer.

import java.io.*;
import java.util.*;
import java.util.concurrent.*;

public class LineCountServer extends Thread {
   private Queue<LineCounter> queue =
      new LinkedList<LineCounter>();
   private CountDownLatch latch;

   public void setRequests(Queue<LineCounter> queue) {
      this.queue = queue;
   }

   public void run() {
      latch = new CountDownLatch(queue.size());
      while (!queue.isEmpty()) {
         final LineCounter counter = queue.remove();
         new Thread(new Runnable() {
            public void run() {
               execute(counter);
               latch.countDown();
            }
         }).start();
      }
      waitOnLatch();
   }

   private void waitOnLatch() {
      while (true) {
         try {
            latch.await();
            break;
         } catch (InterruptedException e) {
         }
      }
   }

   protected void execute(LineCounter counter) {
      counter.run();
      synchronized (this) {
         System.out.println(counter.getFilename() + " " +
                            counter.getCount());
      }
   }
}

The CountDownLatch is a simple class to understand and use, one that you should take advantage of in your multithreaded applications!

About the Author

Jeff Langr is a veteran software developer with over a quarter century of professional software development experience. He's authored two books and over 50 published articles on software development, including Agile Java: Crafting Code With Test-Driven Development (Prentice Hall) in 2005. You can find out more about Jeff at his site, http://langrsoft.com, or you can contact him via email at jeff at langrsoft dot com.





Page 2 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel