208. Latches

A latch is a Java synchronizer that allows one or more threads to wait until a bunch of events in other threads has completed. It starts from a given counter (commonly representing the number of events that should be waited), and each event that completes is responsible for decrementing the counter. When the counter reaches zero all the waiting threads can pass through. This is the terminal state of a latch. A latch cannot be reset or reused, so the waited events can happen only once. The following diagram shows, in four steps, how a latch with three threads works:

In API terms, a latch is implemented using java.util.concurrent.CountDownLatch.

 The initial counter is set in the CountDownLatch constructor as an integer. For example, a CountDownLatch with a counter equal to 3 can be defined as follows:

CountDownLatch latch = new CountDownLatch(3);

All threads that call the await() method will be blocked until the counter reaches zero. So, a thread that wants to be blocked until the latch reaches the terminal state will call await(). Each event that completes can call the countDown() method. This method decrements the counter with one value. Until the counter becomes zero, the threads that called await() are still blocked.

A latch can be used for a wide range of problems. For now, let's focus on our problem that should simulate the process of starting a server. The server is considered started after its internal services have started. Services can be started concurrently and are independent of each other. Starting a server is a process that takes a while and requires us to start all the underlying services of that server. Therefore, the thread that finalizes and validates the server start should wait until all server services (events) have started in other threads. If we assume that we have three services, we can write a ServerService class as follows:

public class ServerInstance implements Runnable {

private static final Logger logger =
Logger.getLogger(ServerInstance.class.getName());

private final CountDownLatch latch = new CountDownLatch(3);

@Override
public void run() {
logger.info("The server is getting ready to start ");
logger.info("Starting services ... ");

long starting = System.currentTimeMillis();

Thread service1 = new Thread(
new ServerService(latch, "HTTP Listeners"));
Thread service2 = new Thread(
new ServerService(latch, "JMX"));
Thread service3 = new Thread(
new ServerService(latch, "Connectors"));

service1.start();
service2.start();
service3.start();

try {
latch.await();
logger.info(() -> "Server has successfully started in "
+ (System.currentTimeMillis() - starting) / 1000
+ " seconds");
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
// log ex
}
}
}

First, we define a CountDownLatch with a counter of three. Second, we start the services in three different threads. Finally, we block this thread via await(). Now, the following class simulates the starting process of services via random sleep:

public class ServerService implements Runnable {

private static final Logger logger =
Logger.getLogger(ServerService.class.getName());

private final String serviceName;
private final CountDownLatch latch;
private final Random rnd = new Random();

public ServerService(CountDownLatch latch, String serviceName) {
this.latch = latch;
this.serviceName = serviceName;
}

@Override
public void run() {

int startingIn = rnd.nextInt(10) * 1000;

try {
logger.info(() -> "Starting service '" + serviceName + "' ...");

Thread.sleep(startingIn);

logger.info(() -> "Service '" + serviceName
+ "' has successfully started in "
+ startingIn / 1000 + " seconds");

} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
// log ex
} finally {
latch.countDown();

logger.info(() -> "Service '" + serviceName + "' running ...");
}
}
}

Each service that started successfully (or failed) will decrement the latch via countDown(). Once the counter reaches zero, the server is considered started. Let's call it:

Thread server = new Thread(new ServerInstance());
server.start();

Here is a possible output:

[08:49:17] [INFO] The server is getting ready to start

[08:49:17] [INFO] Starting services ...
[08:49:17] [INFO] Starting service 'JMX' ...
[08:49:17] [INFO] Starting service 'Connectors' ...
[08:49:17] [INFO] Starting service 'HTTP Listeners' ...

[08:49:22] [INFO] Service 'HTTP Listeners' started in 5 seconds
[08:49:22] [INFO] Service 'HTTP Listeners' running ...
[08:49:25] [INFO] Service 'JMX' started in 8 seconds
[08:49:25] [INFO] Service 'JMX' running ...
[08:49:26] [INFO] Service 'Connectors' started in 9 seconds
[08:49:26] [INFO] Service 'Connectors' running ...

[08:49:26] [INFO] Server has successfully started in 9 seconds
In order to avoid indefinite waiting, the CountDownLatch class has an await() flavor that accepts a timeout, await​(long timeout, TimeUnit unit). If the waiting time elapses before the count reaches zero, this method returns false.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.139.224