假设一个场景:
地铁站,地铁到站,司机打开车门,乘客上下车,车厢乘务员检查所在车厢是否可安全关闭车门,没问题的话就对讲机报告几号车厢准备完毕,司机收到所有车厢乘务员完毕信号才可关闭车门发车。
现实中类似这种并发业务场景,一个或多个线程,要等待另外一组线程执行完毕后,才可继续执行的问题,我们就要考虑使用线程同步工具了。
CountDownLatch简介:
CountDownLatch,一个线程同步工具,其作用与Thread.join()方法类似,让一些线程阻塞直至其他线程执行完成被重新唤醒。主要有三个方法:
(1) 构造函数,初始化state的值,state等于同步线程数。
(2) await(),让线程阻塞。
(3) countDown(),计数器(state)减1的方法。
理论很苦涩,下面写一个简单示例便于吸收消化。
简单示例:
老司机
package com.youyouzhixin.demo.countdownlatch;
import java.util.concurrent.CountDownLatch;
/**
*
* 描述:老司机
* @since
*/
public class Driver {
public void closeDoor() {
try {
int count = 6;// 6节车厢
CountDownLatch latch = new CountDownLatch(count);
for (int i = 1; i <= count; i++) {
String trainmanName = i + "号车厢乘务员";
Trainman trainman = new Trainman(trainmanName, latch, i);
trainman.start();
}
long cn = latch.getCount();
System.out.println("当前未就绪车厢数:" + cn);
latch.await();
System.out.println("车门关闭完成!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
车厢乘务员
package com.youyouzhixin.demo.countdownlatch;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
/**
*
* 描述:车厢乘务员
* @since
*/
public class Trainman extends Thread {
private CountDownLatch latch;
private int coachNum;
public Trainman(String name, CountDownLatch latch, int coachNum) {
this.setName(name);
this.latch = latch;
this.coachNum = coachNum;
}
/**
* 乘务员检查车厢关闭状况
*/
public void run(){
long time = (long) 0.0;
try {
Random random = new Random(5+coachNum);
//检查时长
time = random.nextInt(6)*500;
sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println(coachNum+"号车厢耗时:"+time+"毫秒");
System.out.println(this.getName()+"准备完毕");
System.out.println();
latch.countDown();
}
}
}
测试
package com.youyouzhixin.demo.countdownlatch;
public class CountDownLatchTest {
public static void main(String[] args) {
Driver driver = new Driver();
driver.closeDoor();
}
}
结果
思考:
可否把乘务员作为观察者,老司机作为主题理解(老司机把自己的一个成员变量注册到乘务员,而非自己本身)?
续前:
上篇文章《观察者模式(一)——入门学习与示例》中最后问题也可以引入该思想解决。
观察者
package com.youyouzhixin.demo.designpattern.ospattern.observer;
/**
*
* 描述:观察者抽象接口
* @since
*/
public interface Observer {
/**
*
* 描述:呼叫我
* @since
* @param info 通知内容
*/
public void callMe(String info);
/**
*
* 描述:获取观察者名字
* @since
* @return
*/
public String getName();
}
主题
package com.youyouzhixin.demo.designpattern.ospattern.observer;
/**
*
* 描述:主题抽象接口
* @since
*/
public interface Subject {
/**
*
* 描述:观察者注册
* @since
* @param observer
*/
public void registerObserver(Observer observer);
/**
*
* 描述:删除观察者
* @since
* @param observer
*/
public void removeObserver(Observer observer);
/**
*
* 描述:当主题有变化时调用,用于通知观察者
* @since
*/
public void notifyObserver();
}
房东
package com.youyouzhixin.demo.designpattern.ospattern;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import com.youyouzhixin.demo.designpattern.ospattern.observer.Observer;
import com.youyouzhixin.demo.designpattern.ospattern.observer.Subject;
/**
*
* 描述:房东(具体主题)
*
* @since
*/
public class Landlord implements Subject {
private String name;
private ConcurrentMap<String, Observer> observers;
private String houseInfo;
private String messageInfoFormate;
public Landlord(String myname) {
observers = new ConcurrentHashMap<String, Observer>();
houseInfo = "";
name = myname;
messageInfoFormate = name + "最新空房源户型[%s]价位[%s]元。";
}
/**
* 租客登记
*/
public void registerObserver(Observer observer) {
this.observers.put(observer.getName(), observer);
}
/**
* 租客撤销
*/
public void removeObserver(Observer observer) {
observers.remove(observer.getName());
}
/**
* 通知租客
*/
public void notifyObserver() {
try {
long begintime = System.currentTimeMillis();
int i=1;
CountDownLatch latch = new CountDownLatch(observers.size());
for (Observer observer : observers.values()) {
MessageSending messageSending = new MessageSending("客服"+i, latch, observer, houseInfo, i);
messageSending.start();
i++;
}
latch.await();
long endtime = System.currentTimeMillis();
System.out.println("房东消息发送完毕,耗时:"+(endtime-begintime));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
*
* 描述:发布空房源信息
*
* @since
* @param houseInfoNew
*/
public void publishInfo(String... houseInfoNew) {
if (houseInfoNew != null) {
this.houseInfo = String.format(messageInfoFormate,
(Object[]) houseInfoNew);
System.out.println("[" + name + "]发布信息:" + houseInfo);
this.notifyObserver();
}
}
}
租客
package com.youyouzhixin.demo.designpattern.ospattern;
import com.youyouzhixin.demo.designpattern.ospattern.observer.Observer;
/**
*
* 描述:租客
* @since
*/
public class Renter implements Observer {
private String name;
public Renter(String myname) {
this.name = myname;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 最新空房源信息通知
*/
public void callMe(String info) {
System.out.println(name + "接收通知信息:" + info);
}
}
消息发送
package com.youyouzhixin.demo.designpattern.ospattern;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import com.youyouzhixin.demo.designpattern.ospattern.observer.Observer;
/**
*
* 描述:消息发送
* @since
*/
public class MessageSending extends Thread {
private CountDownLatch latch;
private Observer observer;
private String houseInfo;
private int num;
public MessageSending(String name, CountDownLatch latch, Observer observer, String houseInfo, int num) {
this.setName(name);
this.latch = latch;
this.observer = observer;
this.houseInfo = houseInfo;
this.num = num;
}
/**
* 消息通知
*/
public void run(){
int time = 0;
try {
Random random = new Random(num+7);
time = random.nextInt(6)*1000;
sleep(time);
observer.callMe(houseInfo);
} catch (Exception e) {
e.printStackTrace();
} finally{
System.out.println(this.getName()+"发送消息完毕,耗时:"+time+"毫秒");
System.out.println();
latch.countDown();
}
}
}
测试
package com.youyouzhixin.demo.designpattern.ospattern;
public class OSpatternTest {
public static void main(String[] args) {
try {
Landlord landlord = new Landlord("房东甲");
Renter renterA = new Renter("租客A");
landlord.registerObserver(renterA);
Renter renterB = new Renter("租客B");
landlord.registerObserver(renterB);
Renter renterC = new Renter("租客C");
landlord.registerObserver(renterC);
landlord.publishInfo("单间", "950");
System.out.println();
Thread.sleep(5000);
landlord.publishInfo("一房一厅", "1100");
System.out.println();
Thread.sleep(5000);
landlord.publishInfo("二房一厅", "1300");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
温馨提醒:以上只是个人理解,如有不对之处还请各位大神指正。