观察者模式(二)——多线程与CountDownLatch浅析

假设一个场景:

地铁站,地铁到站,司机打开车门,乘客上下车,车厢乘务员检查所在车厢是否可安全关闭车门,没问题的话就对讲机报告几号车厢准备完毕,司机收到所有车厢乘务员完毕信号才可关闭车门发车。

现实中类似这种并发业务场景,一个或多个线程,要等待另外一组线程执行完毕后,才可继续执行的问题,我们就要考虑使用线程同步工具了。

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();
		}
		
		
	}

}



温馨提醒:以上只是个人理解,如有不对之处还请各位大神指正。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值