码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2014-08-11 00:10:21      阅读:274      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   color   java   使用   os   io   

(1)项目的需求

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

例如:

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

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

bubuko.com,布布扣

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

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

bubuko.com,布布扣

(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

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

标签:style   blog   http   color   java   使用   os   io   

原文地址:http://www.cnblogs.com/driverwjd/p/3903643.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!