zoukankan      html  css  js  c++  java
  • 交通灯管理系统

    (1)项目的需求

    模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:

    例如:

    由南向而来去往北向的车辆 ---- 直行车辆

    由西向而来去往南向的车辆 ---- 右转车辆

    由东向而来去往南向的车辆 ---- 左转车辆

    1

    平时开车过十字路口红绿灯的时候,也知道红绿灯运行的顺序

    (1)任何方向的车,向右边转弯的时候,是不需要看红绿灯

    (2)在十字路口,相对方向的红绿灯的工作方式是一样的,南相对与北,东相对与西,这把它分成两对

    (3)红绿灯顺序,一对直行通道绿灯直行车辆,等直行变红,还是这对的车辆可以左转,等左转变红,就轮到下一对了.所以在设计程序的时候,只需要考虑一对的红绿灯情况即可.

    Ø 信号灯忽略黄灯,只考虑红灯和绿灯。

    Ø 应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。

    Ø 具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。

    注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。

    Ø 每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。

    Ø 随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。

    (2)代码的思路与体现

    首先对这个项目所设计到的名词都提炼出来,包括,汽车,马路,交通灯,通知交通灯的系统,张孝祥老师说了一句面向对象设计最经典的话,谁拥有数据,那么谁就来对外提供操作这些数据的方法,通过对这句话的理解,上面涉及到的名词,汽车是在马路上跑的,那么马路就得提供自己马路上汽车的数量,只有提供了汽车数量的变化,才能让红绿灯友好的运作起来.

    Road类:构造方法初始化的时候要加上那路的线路,同时在路这个类中得提供一个容器去装路上的汽车.

    package cn.interview.traffic;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    import java.util.concurrent.Executor;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class Road {
        //交通工具集合,用来装交通工具的
        List<String> vehicles = new ArrayList<String>();
        //name是十字路上方向对方向路的名字
        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=0;i<1000;i++){
                        try {
                            Thread.sleep((new Random().nextInt(10)+i) * 1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        vehicles.add(Road.this.name+"_"+i);
                    }
                }
                 
             });
             //在Road对象的构造方法中启动一个定时器,每隔一秒检查该方向上的灯是否为绿
           ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
           timer.scheduleAtFixedRate(
                   new Runnable(){
    
                    public void run() {
                        if(vehicles.size()>0){
                            boolean lighted = Lamp.valueOf(Road.this.name).isLighted();
                            if(lighted == true){
                                System.out.println(vehicles.remove(0)+" is traversing");
                            }
                        }
                    }
                       
                   },
                   1,
                   1,
                  TimeUnit.SECONDS);
        }
    }

    Lamp类:2种状态,true(绿灯),false(红灯),还要提供红绿灯变化的情况,绿灯亮的情况必须要保证对立两个方向的绿灯都亮,灯红的时候同时要包装对应方向的灯也要变红,同时等变红的时候,在方法中得有一个返回值,就是把红灯变成绿灯,这样这个交通灯系统才有一个良好的循环.

    package cn.interview.traffic;
    // S2N S2W  E2W   E2S  N2S  N2E   W2E   W2N   S2E   E2N   N2W  W2S
    public enum Lamp {
        //12个方向的枚举对象
        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 Lamp(){
            
        }
        private boolean lighted;
        //表示反方向的灯,用String类型,方便传入枚举的构造方法中去
        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个方向的灯可以通过");
        }
        //红灯状态,有返回值,返回下一个灯
        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;
        }
    }

    LampController类:我们默认S2N方向的灯开始是绿灯,这里如何进行交通灯时间上的切换,用到了多线程的技术,我会在总结的时候,把枚举和多线程的知识补充上去.

    package cn.interview.traffic;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    
    
    public class LampController {
        private Lamp currentLamp;
        public LampController(){
            currentLamp = Lamp.S2N;
            currentLamp.light();
            //这个也是用多线程技术,在控制交通灯变化的时间
            ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
            timer.scheduleAtFixedRate(
                    new Runnable(){
    
                        public void run() {
                            currentLamp = currentLamp.blackOut();
                        }
                        
                    }, 
                    10,
                    10,
                    TimeUnit.SECONDS);
        }
    }

    MainClass类: 直接用数组new出12条路,然后在把newRoad类,系统就能运行了.

    package cn.interview.traffic;
    
    public class MainClass {
        public static void main(String[] args) {
            //12个路口定义一个数组,把这些路口加到路上去
            String[] directions = {"S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"};
            for(int i=0;i<directions.length;i++){
                new Road(directions[i]);
            }
            //开始跑交通灯了
            new LampController();
        }
    
    }

    (3)总结

    这个项目需要对十字路口红绿灯的运行模式十分的熟悉,在对时间上的处理,比如汽车上路的时间,交通灯变化的时间,要到了JDK1.5之后的新的多线程技术,同时对于枚举的作用,在这个项目中也发挥的淋漓精致,省去了很多繁琐的代码,整个项目感觉

    第一: 前期对于对象的提炼和哪个对象拥有哪些方法,这个真是需要一定的代码功力,才能完美的将对应的方法放到对应的对象去上,

    第二:JDK1.5多线程Executors可以解决时间上的问题

    第三:枚举的使用,简化了代码的书写,同时看完张老师的高新技术后,对于枚举的运用,对于具体问题,把枚举这个类的构造方法设计好,在把相关的参数传入对应的枚举对象中去,这点真心太重要了,在枚举传递参数的时候,一般使用的是String,而后面需要用String去调用方法是行不同的,那么这个时候枚举中的valueOf()就起作用了.

    ----------------------------------------------------------------------------------------------------------------------------------------------------

    通过交通灯的项目,对JDK1.5后,出现的多线程以前新的技术的补充(线程池)

    线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

    Java5的线程池分好多种:具体的可以分为两类,固定尺寸的线程池、可变尺寸连接池。

    在使用线程池之前,必须知道如何去创建一个线程池,在Java5中,需要了解的是java.util.concurrent.Executors类的API,这个类提供大量创建连接池的静态方法,是必须掌握的。

    (1)固定大小的线程池,newFixedThreadPool:

    package cn.thread;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Test01 {
        public static void main(String[] args) {
            //创建容量是5的线程池
            ExecutorService pools = Executors.newFixedThreadPool(3);
            //创建5个线程出来
            Thread t1 = new MyThread();
            Thread t2 = new MyThread();
            Thread t3 = new MyThread();
            Thread t4 = new MyThread();
            Thread t5 = new MyThread();
            //将5个线程装入池子中去
            pools.execute(t1);
            pools.execute(t2);
            pools.execute(t3);
            pools.execute(t4);
            pools.execute(t5);
            //关闭线程池
            pools.shutdown();
        }
    }
    class MyThread extends Thread{
        public void run(){
            System.out.println(Thread.currentThread().getName()+" is running....");
            /*线程池中参数为5的时候,输出的情况
              pool-1-thread-2is running....
              pool-1-thread-4is running....
              pool-1-thread-1is running....
              pool-1-thread-3is running....
              pool-1-thread-5is running....
             * 
             * 参数为3,输出的情况
             *  pool-1-thread-2 is running....
                pool-1-thread-3 is running....
                pool-1-thread-1 is running....
                pool-1-thread-2 is running....
                pool-1-thread-3 is running....
             */
        }
    }

    观察参数是3和5的时候,sop输出的情况,newFixedThreadPool是指定在线程池中,有几个线程可以住在这里面,超过了这个数字的话,线程就住不进来了,其次,加入线程池的线程属于托管状态,线程的运行不受加入顺序的影响。

    (2)单任务线程池,newSingleThreadExecutor

    //ExecutorService pools = Executors.newFixedThreadPool(3);
            ExecutorService pools = Executors.newSingleThreadExecutor();
    sop的内容  
     pool-1-thread-1 is running....
    pool-1-thread-1 is running....
    pool-1-thread-1 is running....
    pool-1-thread-1 is running....
    pool-1-thread-1 is running....

    可以看出,每次调用execute方法,其实最后都是调用了thread-1的run方法。查看API,就能发现execute方法中,就一个Runnable可运行的任务.而根据以前的多线程的知识,实现多线程的两种方式,其实最后都和Runnable这个接口搭上关系的.

    1

    (3)三、可变尺寸的线程池,newCachedThreadPool:

    这种方式的特点是:可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。

    (4)四、延迟连接池,newScheduledThreadPool  上面的交通灯和汽车时间的变化,就是用到的这个

    package cn.thread;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class Test2 {
        public static void main(String[] args) {
            //这个线程池的特点就是可以在给定的时间后运行
            ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
            Thread t1 = new MyThread02();
            Thread t2 = new MyThread02();
            Thread t3 = new MyThread02();
            pool.execute(t1);
            pool.schedule(t2,30,TimeUnit.SECONDS);//t1执行完毕后,30秒后,t2才执行T
            pool.scheduleAtFixedRate(t3,100,100,TimeUnit.SECONDS);//100秒后执行,如果还有的话在100+100执行,还有的话100+100*n执行
            pool.shutdown();
        }
    
    }
    class MyThread02 extends Thread{
        public void run(){
            System.out.println(Thread.currentThread().getName()+"is running...");
        }
    }

    这个线程池可以设置线程启动的时间,具体线程启动时间如何安排的,我已经在代码中注释起来了.

  • 相关阅读:
    机器学习的定义和分类
    选股
    mysql修改密码
    快速排序
    php的错误类型
    MySQL数据库优化
    库存超卖问题
    循环处理
    kafka安装配置
    JavaScript、jQuery杂记
  • 原文地址:https://www.cnblogs.com/driverwjd/p/3903643.html
Copyright © 2011-2022 走看看