Java multi-threaded programming-CountDownLatch

Foreword:

This article is based on my personal understanding of Chapter 5 of "Java Multi-Threaded Programming Practical Guide - Core Chapter". The source code is excerpted from the author's source code. The source code will add your own understanding. The reading notes are currently being updated by the author as follows, "Java Multi-Threaded Programming Practical Guide - Core Chapter", "How Tomcat Works", and then "spring source code" Interpretation.

CountDownLatch:

CountDownLatch can be used to implement one (or more) threads to wait for other threads to complete a specific set of operations before continuing to run. This set of operations is called prerequisite operations. CountDownLatch internally maintains a counter of the user's unfinished prerequisite operations. Each time CountDownLatch.countDown() is executed, the counter value of the corresponding instance will be reduced by 1. CountDownLatch.await() is equivalent to a protected method, which protects The condition is "counter value is 0", so when the counter value is not 0, the execution thread will be suspended, and countDown() is equivalent to a notification method that wakes up all waiting threads on the corresponding instance when the counter value reaches 0.

One of the points is that when the value of the counter reaches 0, the value of the counter will no longer change. Calling countDown() again at this time will not cause an exception to be thrown, and the await thread will not be suspended, so countDownLatch The use is one-time, and a countDownLatch instance can only wait and wake up once.

Practical case:

The book introduces a practical case of a web server. When it starts, it needs to start several services that are time-consuming to start. In order to reduce the time-consuming process of server startup as much as possible, the server will use special working threads to start services in a concurrent manner. However, after all startup operations are completed, the server needs to determine the status of these services to check whether the server startup is successful. The server is considered to be successfully started only when all services are started successfully.

Personally, I think this web server example is very good.

Basic explanation of code:

1. The main process of ServiceStarter is the main process, start all services -> detect all services -> finally determine whether the output is started successfully.

2. Start all services: Encapsulated in the startServices of serviceManager, a CountDownLatch is instantiated, and three services A and BC are instantiated.

3. The three services A, B, and C respectively inherit AbstractService, and AbstractService also implements the interface service.

Among them, AbstractService reserves the doStart function for subclasses, and the subclasses can implement their business content on their own, so A, B, and C inherit AbstractService and rewrite doStart. At the same time, subclasses A, B, and C can call start, stop, and isStart of the parent class AnstractService to start/stop/check threads.

4. Detect all services: Encapsulate it in checkServiceStatus of serviceManager, perform CountDownLatch.await(), wait for all threads to start, and set the flag allIsOK=true

5. Finally, output the corresponding log according to the flag allIsOK.

Code:

public class CaseRunner5_2 {

  public static void main(String[] args) {
    ServerStarter.main(args);
  }

}
public class ServerStarter {

  public static void main(String[] args) {
    // 省略其他代码

    // 启动所有服务
    ServiceManager.startServices();

    // 执行其他操作

    // 在所有其他操作执行结束后,检测服务启动状态
    boolean allIsOK;
    // 检测全部服务的启动状态
    allIsOK = ServiceManager.checkServiceStatus();

    if (allIsOK) {
      System.out.println("All services were sucessfully started!");
      // 省略其他代码
    } else {
      // 个别服务启动失败,退出JVM
      System.err.println("Some service(s) failed to start,exiting JVM...");
      System.exit(1);
    }
    // ...
  }
}
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;

public class ServiceManager {
  static volatile CountDownLatch latch;
  static Set<Service> services;

  public static void startServices() {
    services = getServices();
    for (Service service : services) {
      service.start();
    }
  }

  public static boolean checkServiceStatus() {
    boolean allIsOK = true;
    // 等待服务启动结束
    try {
      latch.await();
    } catch (InterruptedException e) {
      return false;
    }

    for (Service service : services) {
      if (!service.isStarted()) {
        allIsOK = false;
        break;
      }
    }
    return allIsOK;
  }

  static Set<Service> getServices() {
    // 模拟实际代码
    latch = new CountDownLatch(3);
    HashSet<Service> servcies = new HashSet<Service>();
    servcies.add(new SampleServiceC(latch));
    servcies.add(new SampleServiceA(latch));
    servcies.add(new SampleServiceB(latch));
    return servcies;
  }
}
public interface Service {
	void start();
	void stop();
	boolean isStarted();
}
import java.util.concurrent.CountDownLatch;

public abstract class AbstractService implements Service {
  protected boolean started = false;
  protected final CountDownLatch latch;

  public AbstractService(CountDownLatch latch) {
    this.latch = latch;
  }

  @Override
  public boolean isStarted() {
    return started;
  }

  // 留给子类实现的抽象方法,用于实现服务器的启动逻辑
  protected abstract void doStart() throws Exception;

  @Override
  public void start() {
    new ServiceStarter().start();
  }

  @Override
  public void stop() {
    // 默认什么也不做
  }

  class ServiceStarter extends Thread {
    @Override
    public void run() {
      final String serviceName = AbstractService.this.getClass()
          .getSimpleName();
      Debug.info("Starting %s", serviceName);
      try {
        doStart();
        started = true;
      } catch (Exception e) {
        e.printStackTrace();
      } finally {
        latch.countDown();
        Debug.info("Done Starting %s", serviceName);
      }
    }
  }
}
import java.util.concurrent.CountDownLatch;


public class SampleServiceA extends AbstractService {

	public SampleServiceA(CountDownLatch latch) {
		super(latch);

	}

	@Override
	protected void doStart() throws Exception {
		// 模拟实际操作耗时
		Tools.randomPause(1000);

		// 省略其他代码
	}

}
import java.util.concurrent.CountDownLatch;

public class SampleServiceB extends AbstractService{
	
	public SampleServiceB(CountDownLatch latch) {
	  super(latch);
  }

	@Override
  public void doStart() throws Exception {
		// 模拟实际操作耗时
		Tools.randomPause(2000);
		
		// 省略其他代码
	  
  }

}
import java.util.Random;
import java.util.concurrent.CountDownLatch;

public class SampleServiceC extends AbstractService {

	public SampleServiceC(CountDownLatch latch) {
		super(latch);
	}

	@Override
	public void doStart() throws Exception {
		// 模拟实际操作耗时
		Tools.randomPause(2000);
		
		// 省略其他代码

		// 模拟服务器启动失败
		final Random random = new Random();
		int rand = random.nextInt(1000);
		if (rand <= 500) {
			throw new RuntimeException("test");
		}
	}

}

Guess you like

Origin blog.csdn.net/u012895183/article/details/133269244