JavaSE——Java交通系统
2013-05-22 17:29
148 查看
交通系统仿真
摘要:
本文首先对张孝祥老师交通灯系统的面试题,根据自己的理解进行了解析,并给出了自己编写的代码. 之后, 本文根据视频中张老师的解析, 比较了自己的解法和张老师解法的不同, 总结了优劣. 最后给出了总结.I. 需求:
1. 模拟车辆过过十字路口, 车辆随机驶入路口, 判断红绿灯是否可以通行, 不考虑黄灯2. 先放一对方向(如North-South)直行,再左转, 之后放行另一对方向直行,再左转
3. 右转不受限
4. 车辆过街要1s
II. 思考过程:
(此处为自己编写的思路,与张老师视频中的思路略有不同)
1. 红绿灯种类有限, 而且次序变化是固定的, 考虑将灯的变化定义成一个枚举类SignalEnum2. 需要有一个控制中心去控制灯的变化, 定义一个类ControlCenter
3. 车辆随机驶入路口, 而车辆在此时就有了明确的行驶和转向, 这个类型可以和SignalEnum里面定义的类型匹配
——————————————————————————————————————————————————
1.1 考虑对面方向灯变化的一致性, 右转无需设定, 枚举一共有4种变化类型: NORTHSOUTH_STR, NORTHSOUTH_LEFT, EASTWEST_STR, EASTWEST_LEFT
1.2 考虑灯有红,绿两种情况, 用一个boolean green; 来记录红绿, green=true为绿, green=false为红
1.3 需要将green设置为private并提供isGreen()/ setGreen()两个方法判断/设置灯的红绿
1.4 为方便可读性, 定义setRed()函数, 于setGreen()操作相反
1.5 考虑灯的变绿顺序变化, 在枚举类中定义 abstract nextGreenSignal() 函数, 返回下一个变绿的枚举类
1.6 模拟1s中过道, 程序等1s; await(1s)
——————————————————————————————————————————————————
2.1 控制中心需要调用枚举类中的枚举元素,并通过isGreen/setGreen/setRed对SignalEnum元素的灯红绿变化控制
2.2 控制中心内提供greenDuration(); 方法, 控制路灯的时间
2.3 控制中心需要按顺序循环实现对SignalEnum元素红绿灯控制
2.4 因为每辆车要1s中的时间过街, currentGreenSignal 变红后要等1s再让下一个灯变绿
2.5 可将ControlCenter作为单独的线程存在
——————————————————————————————————————————————————
3.1 车辆随机驶入路口, 可以看做是一个线程进入程序运行
3.2 因为, 车辆需要根据自己的行驶判断相应SignalEnum元素红绿灯, 不放直接将 SignalEnum implements Runnable, 将SignalEnum.Xxxx传入线程, 让线程判断SignalEnum中的green
3.3 控制台也对SignalEnum中元素的green成员进行操作,所以加同步可以保证多线程对公共资源操作的数据安全
3.4 来一个路口的车可能很多, 所以会有多个一类的线程同时存在, 需要定义一个condition让他们红灯await, 当控制台端变绿时, 控制台端 signalAll
3.5 由于有四个方向, 更好的是将Lock,Condition和SignalEnum中的枚举类一一绑定, 这样, 控制台没换到一个currentGreenSignal时,就同步到等一类灯变绿的线程车上,去唤醒这一类的线程
3.6 考虑右转, 在枚举类型里添加一个 NSEW_RIGHT标记所有右转的车辆的Runnable操作对象, 此对象内部复写isGreen;setRed
3.7 传入线程的SignalEnum在线程中不支持, 建立CarThread extends Thread, 创建一个新的构造函数
III 代码:
1. SignalEnum枚举类定义交通灯的种类及功能
package cn.itcast.trafficSystemSimulation; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.*; /* * 1. 用枚举定义不同的信号灯情况, 分为 NORTHSOUT_STR,NORTHSOUTH_LEFT,EASTWEST_STR, * EASTWEST_LEFT,(NSEW_RIGHT) * 2. 需要将信号等的情况传给CarThread作为线程的Runnable的对象使用,实现Runnable --> 3.2 * 3. NSEW_RIGHT这两种情况只是为了给Car传入用时,考虑车辆右行总是无需停车的状况准备的 --> 3.6 * 对控制台控制红绿灯的方面没有用的 */ public enum SignalEnum implements Runnable{ // --> 3.2 // 枚举列表开始 |--- --> 1.1 NORTHSOUTH_STR{ public SignalEnum nextGreenSignal(){ return NORTHSOUTH_LEFT; } }, NORTHSOUTH_LEFT{ public SignalEnum nextGreenSignal(){ return EASTWEST_STR; } }, EASTWEST_STR{ public SignalEnum nextGreenSignal(){ return EASTWEST_LEFT; } }, EASTWEST_LEFT{ public SignalEnum nextGreenSignal(){ return NORTHSOUTH_STR; } }, NSEW_RIGHT{ // --> 3.6 public SignalEnum nextGreenSignal(){ return NSEW_RIGHT; } public boolean isGreen(){return true;} public void setRed(){}; }; // 枚举列表结束 ---| // ---------------------- 控制台部分需要设置红绿灯部分方法 ----------------------------- // private boolean green=false; // --> 1.2 public boolean isGreen(){return green;} // --> 1.3 public void setGreen(){green=true;} // --> 1.3 public void setRed(){green=false;} // --> 1.4 public abstract SignalEnum nextGreenSignal(); // --> 1.5 // --------------------- Runnable 接口部分复写内容 ---------------------------------- // /* * 复写run,调用checkSignal方法 */ public void run(){ try{ checkSignal(); }catch(InterruptedException e){ e.printStackTrace(); } } private Lock lock=new ReentrantLock(); // --> 3.5 private Condition stopCondition=lock.newCondition(); // --> 3.5 /* * checkSignal用来件事 green的值,如果false,await; * 此处同步ControlCenter内部分代码,如果green=true, await线程唤醒,且可以出循环继续执行 */ public void checkSignal() throws InterruptedException{ lock.lock(); // --> 3.3 try{ while(!isGreen()){ System.out.println(this+"'s " +Thread.currentThread().getName()+" wait for red." +" Green?"+isGreen()); stopCondition.await(); // --> 3.4 System.out.println(this+"'s " +Thread.currentThread().getName()+" notified for green." +" Green?"+isGreen()); } stopCondition.await(1000,TimeUnit.MILLISECONDS); // --> 1.6 System.out.println(this+"'s " +Thread.currentThread().getName()+ " acrossed"); }finally{lock.unlock();} // --> 3.3 } /* * getCondition返回一个对应枚举元素的Lock */ public Lock getLock(){ return lock; } /* * getCondition返回一个对应枚举元素Lock的Condition */ public Condition getCondition(){ return stopCondition; } }
2. ControlCenter实现对枚举类SignalEnum的操作
package cn.itcast.trafficSystemSimulation; import java.util.concurrent.locks.Lock; /* * ControlCenter实现对枚举类SignalEnum的操作 * - 实现红绿灯设置 * - 实现灯按顺序循环 * - 实现对CarThread的唤醒 */ public class ControlCenter implements Runnable{ // --> 2.6 public void greenDuration(){ // --> 2.2 try { Thread.sleep(5000 ); }catch (InterruptedException e){ e.printStackTrace(); } } public void run(){ SignalEnum currentGreenSignal=SignalEnum.NORTHSOUTH_STR; while(true){ Lock lock=currentGreenSignal.getLock(); lock.lock(); try{ currentGreenSignal.setGreen(); // --> 2.1 System.out.println("-------Signal-------" +currentGreenSignal+" is green!"); currentGreenSignal.getCondition().signalAll(); // --> 3.4 }finally{ lock.unlock(); } this.greenDuration(); // --> 2.2 currentGreenSignal.setRed(); // --> 2.1 System.out.println("--------Signal-------" +currentGreenSignal+" is red!"+" STOP!"); try { Thread.sleep(1000); // --> 2.4 } catch (InterruptedException e) { e.printStackTrace(); } currentGreenSignal=currentGreenSignal.nextGreenSignal(); // --> 2.4 } } }
3. CarThread模拟车过路口
package cn.itcast.trafficSystemSimulation; /* * CarThread方法继承Thread * - 需传入一个Enum类型,要重建构造函数 */ public class CarThread extends Thread { // --> 3.7 public CarThread(SignalEnum target,String name){ // --> 3.7 super(target,name); System.out.println(this+" comes here."); } public String toString(){ return "Car:"+getName(); } }
IV 演示:
package cn.itcast.trafficSystemSimulation; /* * TrafficSystemSimulation演示结果 Car 0-5 对应 CarThread0-5 * Car 0-5 从不同方向同时来到路口 */ public class TrafficSystemSimulation { public static void main(String[] args) { ControlCenter ctrlCenter=new ControlCenter(); Thread ctrlThread=new Thread(ctrlCenter); CarThread CarThread0=new CarThread(SignalEnum.EASTWEST_STR,"Car 0"); CarThread CarThread1=new CarThread(SignalEnum.NORTHSOUTH_LEFT,"Car 1"); CarThread CarThread2=new CarThread(SignalEnum.EASTWEST_STR,"Car 2"); CarThread CarThread3=new CarThread(SignalEnum.EASTWEST_LEFT,"Car 3"); CarThread CarThread4=new CarThread(SignalEnum.EASTWEST_STR,"Car 4"); CarThread CarThread5=new CarThread(SignalEnum.NSEW_RIGHT,"Car 5"); ctrlThread.start(); CarThread0.start(); CarThread1.start(); CarThread2.start(); CarThread3.start(); CarThread4.start(); CarThread5.start(); } }
输出结果:
Car:Car 0 comes here.
Car:Car 1 comes here.
Car:Car 2 comes here.
Car:Car 3 comes here.
Car:Car 4 comes here.
Car:Car 5 comes here.
-------Signal-------NORTHSOUTH_STR is green!
EASTWEST_STR's Car 0 wait for red. Green?false
EASTWEST_STR's Car 2 wait for red. Green?false
NORTHSOUTH_LEFT's Car 1 wait for red. Green?false
EASTWEST_LEFT's Car 3 wait for red. Green?false
EASTWEST_STR's Car 4 wait for red. Green?false
NSEW_RIGHT's Car 5 acrossed
--------Signal-------NORTHSOUTH_STR is red! STOP!
-------Signal-------NORTHSOUTH_LEFT is green!
NORTHSOUTH_LEFT's Car 1 notified for green. Green?true
NORTHSOUTH_LEFT's Car 1 acrossed
--------Signal-------NORTHSOUTH_LEFT is red! STOP!
-------Signal-------EASTWEST_STR is green!
EASTWEST_STR's Car 0 notified for green. Green?true
EASTWEST_STR's Car 2 notified for green. Green?true
EASTWEST_STR's Car 4 notified for green. Green?true
EASTWEST_STR's Car 4 acrossed
EASTWEST_STR's Car 0 acrossed
EASTWEST_STR's Car 2 acrossed
--------Signal-------EASTWEST_STR is red! STOP!
-------Signal-------EASTWEST_LEFT is green!
EASTWEST_LEFT's Car 3 notified for green. Green?true
EASTWEST_LEFT's Car 3 acrossed
--------Signal-------EASTWEST_LEFT is red! STOP!
-------Signal-------NORTHSOUTH_STR is green!
--------Signal-------NORTHSOUTH_STR is red! STOP!
-------Signal-------NORTHSOUTH_LEFT is green!
--------Signal-------NORTHSOUTH_LEFT is red! STOP!
-------Signal-------EASTWEST_STR is green!
--------Signal-------EASTWEST_STR is red! STOP!
V. 与张老师代码比较
1. 张老师思路:
a. 把每个路口的车等待和放行动作用集合Road的存和取完成, 实现FIFO(这一点比我的程序考虑的更好一点)a.1 这个集合包含两个线程, 1个线程负责阻车,一个线程负责把放车(此处用到JDK1.5新的线程方法)
a.2 阻车线程是通过随机间隔时间向集合添加元素的方式实现的, 放车是通过线程以一定的频率监测红绿灯来实现的
b. 也用了一个枚举Lamp来表示灯的种类(不过,这里面张老师的枚举把所有的都举出以opposite去关联对面的车)
c. 也用了一个控制台LampController来控制灯的变化,这里用了一个Executor去以固定的频率转换绿的灯
2. 本质的区别:
a. 我的程序的思路是让等的变化去唤醒车辆线程, 张老师的程序是让没个Road的放车线程去每隔1s监测红绿灯的变化b. 张老师的程序用了一个集合的思想表现出车排队, 最多有2倍的Road的种类(12)的线程(24),可以FIFO, 而我的程序没有做到这一点, 而是让每一个车作为一个线程,来多少个车就新开多少个线程, 当然这些线程多数处于等待状态, 唤醒时无法控制唤醒状态(相比较,张老师集合的思路更好, 一个推广是可以做双车道的优化)
VI. 总结
1. 程序还可以根据集合的思想进行优化2. JDK1.5多线程的部分需要学习
3. 枚举类内的可读性和等转换的运用还是比较满意的
相关文章推荐
- 黑马程序员--Java学习247K面试题之交通灯管理系统
- 黑马程序员 Java自学总结二十一 张老师7K面试题之交通灯管理系统
- 黑马程序员_java面试题总结【1】(交通灯管理系统)
- 黑马程序员 java学习笔记——交通灯管理系统
- 黑马程序员-java学习笔记之交通灯管理系统
- java--交通灯管理系统
- 黑马程序员--Java 7K面试题之交通灯管理系统
- java7k面试题之交通灯管理系统
- 黑马程序员-java面试题-交通灯管理系统
- Java实例讲解-交通灯管理系统
- 黑马程序员-Java项目之交通灯系统
- java实训——交通灯管理系统
- 黑马程序员_Java学习日记27_交通灯管理系统
- Java基础(十六)——项目实战——交通灯管理系统
- 黑马程序员--交通灯管理系统--java学习日记15(7K)
- javaSE_8系列博客——Java语言的特性(四)--注解--(4)-- 类型注解和可插拔类型系统
- 黑马程序员 Java面向对象——交通灯管理系统
- 黑马程序员--JAVA交通灯管理系统项目
- java--交通灯管理系统
- 黑马程序员_java_基础加强_模拟交通灯管理系统