---------android培训、java培训、期待与您交流! ---------
张孝祥老师的例子---交通灯管理系统,它的需求如下:
异步随机生成按照各个路线行驶的车辆。
例如:
由南向而来去往北向的车辆 ---- 直行车辆
由西向而来去往南向的车辆 ---- 右转车辆
由东向而来去往南向的车辆 ---- 左转车辆
。。。
信号灯忽略黄灯,只考虑红灯和绿灯。
应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。
具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。
注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。
- 每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。
- 随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。
- 不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果
张老师共用四个java类 Lamp.java LampController.java MainClass.java Road.java 来实现。
现在先说说Lamp类。
1. Lamp类表示交通灯,有属性:亮(绿)或不亮(红),每个交通灯有变亮和变黑的方法。一个路口,有三种前进可能:直行、转左、转右,十字路有四个路口,故共有3*4=12条路线,所以,系统中总共要产生12个交通灯。右拐弯的路线本来不受灯的控制,但是为了让程序采用统一的处理方式,故假设有四个右拐弯的灯,这些灯为常亮(常绿)状态。
2. 其他8条路线的灯,它们是两两成对的,可以归为4组,所以,在编程处理时,只要从这4组中各取出一个灯,对这4个灯依次轮询变亮,与这4个灯方向对应的灯则随之一同变化,因此Lamp类中要有一个变量来记住自己相反方向的灯,在一个Lamp对象的变亮和变黑方法中,将对应方向的灯也变亮和变黑。每个灯变黑时,都伴随者下一个灯的变亮,Lamp类中还用一个变量来记住自己的下一个灯。
3. Lamp类对象的数量固定,意义明确,所以适合用枚举来实现。 亮灭状态用lighted 表示,选用S2N、S2W、E2W、E2N这四个方向上的Lamp对象依次轮询变亮,对象中还要有一个opposite 来表示它们相反方向的灯,再用一个next 来表示此灯变亮后的下一个变亮的灯。其他方向上的Lamp对象的next 和opposite 属性设置为null即可,并且"N2S","N2E","W2E","W2N"这四个方向上的 next 和opposite 属性必须设置为null,以防止light和blackOut 互相调用,进入死循环。
- public enum Lamp {
- /*每个枚举元素各表示一个方向的控制灯*/
- S2N("N2S","S2W",false),S2W("N2E","E2W",false),E2W("W2E","E2S",false),E2S("W2N","S2N",false),
- /*下面元素表示与上面的元素的相反方向的灯,它们的“相反方向灯”和“下一个灯”应忽略不计!*/
- N2S(null,null,false),N2E(null,null,false),W2E(null,null,false),W2N(null,null,false),
- /*由南向东和由西向北等右拐弯的灯不受红绿灯的控制,所以,可以假想它们总是绿灯*/
- S2E(null,null,true),E2N(null,null,true),N2W(null,null,true),W2S(null,null,true);
- private Lamp(String opposite,String next,boolean lighted){
- this.opposite = opposite;
- this.next = next;
- this.lighted = lighted;
- }
- /*当前灯是否为绿*/
- private boolean lighted;
- /*与当前灯同时为绿的对应方向*/
- private String opposite;
- /*当前灯变红时下一个变绿的灯*/
- private String next;
- public boolean isLighted(){
- return lighted;
- }
- /**
- * 某个灯变绿时,它对应方向的灯也要变绿
- */
- public void light(){
- this.lighted = true;
- if(opposite != null){
- Lamp.valueOf(opposite).light();
- }
- System.out.println(name() + " lamp is green,下面总共应该有6个方向能看到汽车穿过!");
- }
- /**
- * 某个灯变红时,对应方向的灯也要变红,并且下一个方向的灯要变绿
- * @return 下一个要变绿的灯
- */
- public Lamp blackOut(){
- this.lighted = false;
- if(opposite != null){
- Lamp.valueOf(opposite).blackOut();
- }
- Lamp nextLamp= null;
- if(next != null){
- nextLamp = Lamp.valueOf(next);
- System.out.println("绿灯从" + name() + "-------->切换为" + next);
- nextLamp.light();
- }
- return nextLamp;
- }
- }
- Lamp就是一枚举,里面有12个对象,每个对象有三个属性。假如将opposite 属性也定义为Lamp,会成为Lamp嵌套Lamp,不行的。这里的属性都用String,那个表示亮灭的当然用布尔了。
- 枚举Lamp 有三个方法,只有blackOut() 复杂些。它实现两个功能:1、将opposite 的灯变红,2、next 的灯变绿,最后返回 next 。
- S2N("N2S","S2W",false) 实质是调用了Lamp(String opposite,String next,boolean lighted) 构造方法,.相当于 Lamp.S2N.opposite ="N2S",……,对带构造方法的枚举不了解的,可参考黑马视频 “Java基础加强_实现带有构造方法的枚举”。
-
Road类
每条路线上都会出现多辆车,路线上要随机增加新的车,在灯绿期间还要每秒钟减少一辆车。- 设计一个Road类来表示路线,每个Road对象代表一条路线,总共有12条路线,即系统中总共要产生12个Road实例对象。
- 每条路线上随机增加新的车辆,增加到一个集合中保存。
- 每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口。
- 每个Road对象都有一个name成员变量来代表方向,有一个vehicles成员变量来代表方向上的车辆集合。
- 在Road对象的构造方法中启动一个线程每隔一个随机的时间向vehicles集合中增加一辆车(用一个“路线名_id”形式的字符串进行表示)。
- 在Road对象的构造方法中启动一个定时器,每隔一秒检查该方向上的灯是否为绿,是则打印车辆集合和将集合中的第一辆车移除掉。
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Random;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.ScheduledExecutorService;
- import java.util.concurrent.TimeUnit;
- /**
- * 每个Road对象代表一条路线,总共有12条路线,即系统中总共要产生12个Road实例对象。
- * 每条路线上随机增加新的车辆,增加到一个集合中保存。
- * 每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口。
- * @author 张孝祥 www.it315.org
- *
- */
- public class Road {
- private List<String> vechicles = new ArrayList<String>();
- private String name =null;
- public Road(String name){
- this.name = name;
- //模拟车辆不断随机上路的过程
- ExecutorService pool = Executors.newSingleThreadExecutor();
- pool.execute(new Runnable(){
- public void run(){
- for(int i=1;i<1000;i++){
- try {
- Thread.sleep((new Random().nextInt(10) + 1) * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- vechicles.add(Road.this.name + "_" + i);
- }
- }
- });
- //每隔一秒检查对应的灯是否为绿,是则放行一辆车
- ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
- timer.scheduleAtFixedRate(
- new Runnable(){
- public void run(){
- if(vechicles.size()>0){
- boolean lighted = Lamp.valueOf(Road.this.name).isLighted();
- if(lighted){
- System.out.println(vechicles.remove(0) + " is traversing !");
- }
- }
- }
- },
- 1,
- 1,
- TimeUnit.SECONDS);
- }
- }
- 这里直接贴上了张老师的 Road 代码,包括一些说明。即使是说明,实在没多少可改动,增删的,所以这里作了次“传送门”。
- 当然,代码可以作些解释,它是用了线程池的技术,Collections 是Java5 新增的,用于并行处理,比之前的wait()、notify()和synchronized等,方便安全得多。
- Executor 是具体Runnable任务的执行者,ExecutorService 是线程池管理者,用 ScheduledExecutorService 定时调度线程池。程序中 newScheduledThreadPool(1) --- 创建了一个线程池,里面却只有1个线程,但随后用scheduleAtFixedRate 方法创建一直定期运行的任务,程序中,初始延迟(首次启用)是1秒,后期延迟周期也是1秒。
- 多线程方面,可参考 毕向东的多线程 视频,足足有25个呵;线程池方面,可参考 张孝祥-Java多线程与并发库高级应用 视频。
---------android培训、java培训、期待与您交流! ---------