黑马程序员_交通灯管理系统

(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这个接口搭上关系的.

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

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

黑马程序员_交通灯管理系统,布布扣,bubuko.com

时间: 2024-10-13 21:21:02

黑马程序员_交通灯管理系统的相关文章

黑马程序员___交通灯管理系统

----------- android培训.java培训.java学习型技术博客.期待与您交流! --------- 交通灯管理系统的项目需求 异步随机生成按照各个路线行驶的车辆. 例如: 由南向而来去往北向的车辆 ---- 直行车辆 由西向而来去往南向的车辆 ---- 右转车辆 由东向而来去往南向的车辆 ---- 左转车辆 ... 信号灯忽略黄灯,只考虑红灯和绿灯. 应考虑左转车辆控制信号灯,右转车辆不受信号灯控制. 具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制

黑马程序员--关于交通灯管理系统的分析和思考

------- <a href="http://www.itheima.com" target="blank">android培训</a>.<a href="http://www.itheima.com" target="blank">java培训</a>.期待与您交流! ---------- 1. 交通灯管理系统的项目需求 (1).异步随机生成按照各个路线行驶的车辆 (2).信

黑马程序员java-交通灯管理系统《十》

                   --Java培训.Android培训.iOS培训..Net培训.期待与您交流! -- 1,交通灯管理系统原理与分析 首先明白它的工作原理,由于刚刚学车,大概明白交通灯是如何运作的,一般来说车右转是默认不用看灯的,可以直接右转的, 但有时候当交通有箭头显示的时候又不一样了,所以我们不考虑这种情况.那么默认右转灯是一直绿的.根据东南西北四个方向 的车都有各自的三种路线,按道理,东南西北四个方向都有各自的三个方向的交通灯.从车方面考虑就有12(3x4)种路线,而

黑马程序员_使用Jquery实现AJAX功能

1.AJAX是什么? AJAX技术虽然一直听过,但是真正用起来还是工作有一年了的时候,当时需要实现一个异步更新推送的功能.如:在档案管理系统中,存在代办事项和站内信,而这些模块需要在页面不刷新的情况下定时的反馈给前台待办事项的数量以及简要内容,站内信也是如此.这个时候我认为只有AJAX能够胜任,那AJAX是什么?AJAX是在不重新加载整个页面的情况下与服务器交换数据并更新部分网页的技术. 2.为什么使用Jquery来实现? 学习ASP.NET开始,Jquery的强大已经深深捕获了我,而且我还记着

黑马程序员_集合

集合1.集合和对象数组的区别: 数组的长度不可变,集合的可变: 数组可以存储基本数据类型和对象,集合只能存储对象. 集合的框架图 集合派系的顶层接口Collection1.Collection集合存储对象的方法: add(E e)将元素存储到集合中 addAll(Collection c)将一个集合添加到另外的集合中2.Collection集合提取对象的方法: 通过迭代器iterator中的方法:hasNext()和next()来取出 Iterator it=new iterator(); wh

黑马程序员_学习IOS之字典常用的方法

字典是无序的 数组是有序的.字典分为:可变字典和不可变字典  不可变字典对象 NSDictionary * dict = [[NSDictionary alloc]initWithObjectsAndKeys:@"one",@"1",@"two",@"2",@"three",@"3",@"four",@"4", nil]; //value = ke

黑马程序员_多线程

------- android培训.java培训.期待与您交流! ---------- 多线程1.进程: 正在运行的程序所占有的内存空间,叫做进程. 线程: 一个应用程序中的子程序,对于CPU,子程序可以有一条独立的执行路径,称为线程. 线程特点:依靠一个应用程序,对于其他子程序,独立被CPU执行的2.多线程的好处: 充分利用CPU的资源,程序的同时运行,提高效率3.java中线程创建的两种方法: 第一种: 定义类继承Thread class extends Thread 重写Thread中的r

黑马程序员_ 利用oc的协议实现代理模式

先说下代理模式是什么吧 定义: 为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象 而代理对象可以在客户端和目标对象之间起到中介的作用. 在看过李明杰老师的课程后,我对代理模式有了最初步的理解,虽然还很浅显 但是也明白了代理模式的 一些作用跟用法.首先使用代理模式可以降低耦合度.大大的增强了代码的弹性. 举个例子,小明想看电影,但是没时间买票 于是就拜托小强去买票 最简单的方式就是 建立一个person类(小明) 一个agent类(代理类) ag

黑马程序员_银行业务调度系统

1,项目的具体需求 银行业务调度系统 模拟实现银行业务调度系统逻辑,具体需求如下: 银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口. 有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费.电话费之类业务的客户). 异步随机生成各种类型的客户,生成各类型用户的概率比例为: VIP客户 :普通客户 :快速客户 = 1 :6 :3.   客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速