JAVA高并发编程 - CountDownLatch 使用(二)

72 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第13天,点击查看活动详情

今天继续CountDownLatch 的第二种使用方式,检查死锁。

代码示例

公共抽象类 BaseHealthChecker

public abstract class BaseHealthChecker implements Runnable{
​
    private CountDownLatch latch;
    private String serviceName;
    private boolean serviceUp;
​
    public BaseHealthChecker(String serviceName, CountDownLatch latch)
    {
        super();
        this.latch = latch;
        this.serviceName = serviceName;
        this.serviceUp = false;
    }
    @Override
    public void run() {
        try {
            verifyService();
            serviceUp = true;
        } catch (Throwable t) {
            t.printStackTrace(System.err);
            serviceUp = false;
        } finally {
            if(latch != null) {
                latch.countDown();
            }
        }
    }
​
    public String getServiceName() {
        return serviceName;
    }
​
    public boolean isServiceUp() {
        return serviceUp;
    }
​
    public abstract void verifyService();
}

线程实现类-DatabaseHealthChecker

public class DatabaseHealthChecker extends BaseHealthChecker{
    public DatabaseHealthChecker(CountDownLatch latch) {
        super("DatabaseHealthChecker", latch);
    }
​
    @Override
    public void verifyService() {
        System.out.println("Checking " + this.getServiceName());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(this.getServiceName() + " is UP");
    }
}

线程实现类-NetworkHealthChecker

public class NetworkHealthChecker extends BaseHealthChecker{
    public NetworkHealthChecker( CountDownLatch latch) {
        super("NetworkHealthChecker", latch);
    }
    @Override
    public void verifyService() {
        System.out.println("Checking " + this.getServiceName());
        try
        {
            Thread.sleep(7000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        System.out.println(this.getServiceName() + " is UP");
    }
}

main方法

public class ApplicationStartupUtil {
​
    private ApplicationStartupUtil()
    {
    }
​
    private final static ApplicationStartupUtil INSTANCE = new ApplicationStartupUtil();
​
    public static ApplicationStartupUtil getInstance()
    {
        return INSTANCE;
    }
​
    public static boolean checkExternalServices() throws Exception
    {
        CountDownLatch latch = new CountDownLatch(2);
        List<BaseHealthChecker> services = new ArrayList<BaseHealthChecker>();
        services.add(new NetworkHealthChecker(latch));
        services.add(new DatabaseHealthChecker(latch));
​
        Executor executor = Executors.newFixedThreadPool(services.size());
​
        for(final BaseHealthChecker v : services)
        {
            executor.execute(v);
        }
​
        latch.await();
​
        for(final BaseHealthChecker v : services)
        {
            if( ! v.isServiceUp())
            {
                return false;
            }
        }
        return true;
    }
​
public static void main(String[] args)
        {
            boolean result = false;
            try {
                result = ApplicationStartupUtil.checkExternalServices();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("External services validation completed !! Result was :: "+ result);
        }
}

结果输出

Checking NetworkHealthChecker
Checking DatabaseHealthChecker
DatabaseHealthChecker is UP
NetworkHealthChecker is UP
External services validation completed !! Result was :: true

总结

CountDownLatch不能重新初始化或者修改CountDownLatch内部计数器的值。

CountDownLatchSemaphore在使用AQS的方式上很相似,在同步状态中都是保存的是当前的计数值。

CountDownLatch的作用就是允许一个或多个线程等待其他线程完成操作,看起来有点类似join() 方法,但其提供了比 join() 更加灵活的API。

CountDownLatch可以手动控制在n个线程里调用ncountDown()方法使计数器进行减一操作,也可以在一个线程里调用n次执行减一操作。

join() 的实现原理是不停检查join线程是否存活,如果 join 线程存活则让当前线程永远等待。所以两者之间相对来说还是CountDownLatch使用起来较为灵活。