CountDownLatch :(个人理解)使用阀门值,直到阀门值为0之前,一直阻塞线程。实则使用对象锁,不释放对象锁,一直占用资源,(这里是一个缺点)。阀门值为0时,调用释放对象锁的方法,释放资源。应用的场景,我觉得是需要一些线程先完成的前提下,再使用其他线程。也就是我就是要一些重要的线程(也不是特指重要)完成任务之后,接着再执行其他线程。
Table of Contents
demo1-重写CountDownLatch
demo2-子线程任务完成之后执行主线程
demo3-多个CountDownLatch的应用
本文引用的demo均为转载,其他内容是自己原创。感谢这些demo的提供者。为了部落....O(∩_∩)O哈哈~
这个是我在《java高并发详解》中找到的demo (注:有一些自己的调整):
(readme: 重写了CountDownLatch类以及一些方法。创建了一个自定义异常TimeoutException。应用了一些锁的知识点。(synchronized会一直等待线程释放对象锁而造成阻塞,wait方法会释放对象锁,本程序如果不加该方法的后果就是会一直被阻塞,造成超时。)
四个程序员约定在某个时间到某地聚会,每人都会采用交通工具,最后对按时到达的程序员,输出按时到达。
package com.mzs.entity;
import java.util.concurrent.TimeUnit;
public abstract class Latch {
protected int limit; // 阀门值
protected boolean isLate; // 是否迟到
public Latch(int limit, boolean isLate) {
this.limit = limit;
this.isLate = isLate;
}
/**
* 模拟等待
* @param unit 时间单位
* @param time 预定的到达时间
* @throws InterruptedException 被打断时抛出该异常
* @throws TimeoutException 自定义的时间超时异常,当时间超过预定的到达时间时,抛出该异常
*/
public abstract void await(TimeUnit unit, long time) throws InterruptedException, TimeoutException;
/**
* 阀门值减一
*/
public abstract void countDown();
/**
* 统计未到达的人数
* @return 未到达的人数
*/
public abstract int getUnarrived();
}
package com.mzs.entity;
import java.util.concurrent.TimeUnit;
public class CountDownLatch extends Latch {
public CountDownLatch(int limit, boolean isLate) {
super(limit, isLate);
}
@Override
public void await(TimeUnit unit, long time) throws InterruptedException, TimeoutException {
if (time < 0)
throw new IllegalArgumentException("argument is invalid");
// 表示预定的到达时间
long remainingTime = unit.toNanos(time);
long endTime = System.nanoTime() + remainingTime;
synchronized (this) {
while (limit > 0) {
// 剩余时间小于0,则未按时到达,标记迟到,并抛出自定义超时异常
if (TimeUnit.NANOSECONDS.toMillis(remainingTime) < 0) {
this.isLate = true;
throw new TimeoutException("time is over");
}
// 等待的过程中,被打断时,时间进行新的处理。
this.wait(TimeUnit.NANOSECONDS.toMillis(remainingTime));
// 计算剩余的时间
remainingTime = endTime - System.nanoTime();
}
}
}
@Override
public void countDown() {
synchronized (this) {
// 对阀门值的检查,如果小于0,抛出该异常
if (limit < 0)
throw new IllegalStateException("the number of limit is illegal");
// 阀门值自减,表示已到达
limit--;
// 通知阻塞线程
this.notifyAll();
}
}
@Override
public int getUnarrived() {
return limit;
}
}
package com.mzs.entity;
public class TimeoutException extends Exception {
/**
*
*/
private static final long serialVersionUID = -6499958945796073069L;
private String message;
public TimeoutException(String message) {
super(message);
}
}
package com.mzs.entity;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
public class ProgrammerTravel extends Thread {
private final Latch latch; // 阀门值
private final String programmer; // 程序员
private final String transportation; // 交通工具
private Logger logger = Logger.getLogger(getClass().getName());
public ProgrammerTravel(Latch latch, String programmer, String transportation) {
this.latch = latch;
this.programmer = programmer;
this.transportation = transportation;
}
@Override
public void run() {
logger.info(programmer + " starts to take the transportation [ " + transportation + " ]");
try {
// 模拟程序员到达目的地花费时间的随机性
TimeUnit.SECONDS.sleep(ThreadLocalRandom.current().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
if (!latch.isLate)
logger.info(programmer + " arrived on time!!!");
if (latch.getUnarrived() == 0)
logger.info("all the programmers arrived");
}
public static void main(String[] args) {
// 设置阀门值为4,并标记未迟到
Latch latch = new CountDownLatch(4, false);
new ProgrammerTravel(latch, "Tom", "bike").start();
new ProgrammerTravel(latch, "Selina", "bike").start();
new ProgrammerTravel(latch, "King", "Car").start();
new ProgrammerTravel(latch, "Khan", "Bus").start();
try {
// 设置预定时间为5秒
latch.await(TimeUnit.SECONDS, 5);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (TimeoutException e) {
System.err.println(e);
}
}
}
重写runnable run(),使用java.util的CountDownLatch,实现多个子线程任务完成之后,执行主线程的任务。
package com.mzs.demo1;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Logger;
public class CountDownLatchTest1 {
private static Logger logger = Logger.getLogger("com.mzs.demo1.CountDownLatchTest1");
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(3);
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
logger.info("child thread [" + Thread.currentThread().getName() + "] starts to execute");
try {
Thread.sleep(ThreadLocalRandom.current().nextInt(20));
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("child thread [" + Thread.currentThread().getName() + "] finished");
latch.countDown();
}
};
service.execute(runnable);
}
logger.info("main thread [" + Thread.currentThread().getName() + "] is waiting");
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("main thread [" + Thread.currentThread().getName() + "] starts to execute");
}
}
package com.mzs.demo1;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Logger;
public class CountDownLatchTest2 {
private static Logger logger = Logger.getLogger("com.mzs.demo1.CountDownLatchTest2");
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latch1 = new CountDownLatch(4);
for (int i = 0; i < 4; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
logger.info("运动员" + Thread.currentThread().getName() + "等待裁判命令");
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("运动员" + Thread.currentThread().getName() + "接收裁判命令");
try {
Thread.sleep(ThreadLocalRandom.current().nextInt(20));
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("运动员" + Thread.currentThread().getName() + "到达终点");
latch1.countDown();
}
};
service.execute(runnable);
}
latch.countDown();
try {
latch1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("裁判" + Thread.currentThread().getName() + "评判结果");
}
}