黑马程序员_交通灯面试题

------- android培训java培训、期待与您交流! ----------

 

一、面向对象思想

本例很好的体现了面向对象编程的思想,现实生活中有很多的问题,包括一些有趣的探讨性话题,都可以用面向对象的模型来分析和解决,比如球,船,蚂蚁和木棍,这里的路和车。

在本例中,将路看成一个对象来研究,其中:

固有的属性:name,vehicles ,红绿灯。

固有的动作:路上会新增车辆,车辆会通过路口。

路上的车辆要通过马路,就要遵守一定的交通规则:

                                                

1、东南西北四个方向的车辆右拐不需要有红绿灯,每时每刻都可以行使

2、四对方向:( S2N  --  N2S)  (E2W --  W2E) (S2W --  N2E )  (E2S  --  W2N)

3、四对方向需要遵守交通规则,而且同一时间,只有一对方向可以行使

4、交通灯按照 南 -  东 - 北 - 西 的逆时针方向变化。

 

二、解决方案

1、系统

路:方向,经过以上分析可以得到这个十字路口可以分为12个方向,每个方向上面的车辆都是随机的增加和减少(Daemon线程控制)

车:每个方向上的车辆会随机的增加和减少,可以使用一个容器来容纳

灯:条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口

2、控制

每条路线每隔一秒都会检查控制本路线的灯是否为绿,一个灯由绿变红时,应该将下一个方向的灯变绿。
由于每次都是一对方向同时放行,因此我们需要控制的8个方向,实际上可以简化为4个方向的控制:S2N、S2W、E2W、E2S,这四个作为一圈来循环控制。

3、模块

Road:name,vehicles ,name用方向来表示,Road在1 - 10秒的随机时间内每次产生一辆新车

Lamp:lighted 绿灯状态,opposite 对面的灯,next 下一个灯

 LampController :每隔10秒钟进行放行方向的切换

Main:客户端(application)

三、实现

1、模块Road的实现如下:

public class Road {
	//每一条路每一秒中,增加一辆车
	private List<String> vehicles = new ArrayList<String>();
	
	private String  name ;//使用方向简写来表示
	ExecutorService executor;
	ScheduledExecutorService timer ;
	Random rand ;//张老师写在了循环里面,这样会重复的产生1000个对象,消耗时间和空间
	public Road(final String name){
		this.name = name ;
		this.executor=Executors.newSingleThreadExecutor();//线程池中只有一个线程,不涉及到线程的安全问题 
		this.timer = Executors.newScheduledThreadPool(1);
		rand = new Random();
		
		executor.execute(new Runnable() {//daemon线程,不用客户关注,同时也是不可控
			public void run() {
				for (int i=1;i<1000;i++){
					try {
						Thread.sleep((rand.nextInt(10)+1) * 1000);//产生新车辆的随机时间间隔为1 - 10s之间
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					vehicles.add(name+"_"+i);
					//vehicles.add(Road.this.name+"_"+i);
				}
			}
		});
		
		timer.scheduleAtFixedRate(
									new Runnable() {//同样也是daemon线程,不用客户关注
										public void run() {
											if (vehicles.size() > 0){
												boolean lighted = Lamp.valueOf(Road.this.name).isLighted();//假设这个方向的灯是亮的
												if (lighted){
													System.out.println(vehicles.remove(0)+" is travaling ");//如果这个路口的灯是绿灯,则第一辆车通过,这里每检查一次通过一辆,每个方向都会有这个服务
												}
											}
										}
									},
									1, //过多少秒之后开始干
									1, //每个多少个时间单位又做一次
									TimeUnit.SECONDS);//时间量度
		
	}
}

 

2、模块Lamp的实现

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 Lamp(){}
	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();
		}
	}
	public Lamp blackOut(){
		this.lighted = false;
		if (opposite != null){
			Lamp.valueOf(opposite).blackOut();
			
		}
		Lamp nextLamp = null;
		if (next != null){
			nextLamp = Lamp.valueOf(next);
			nextLamp.light();
		}
		return nextLamp;
	}
}


3、控制器

public class LampController {
	
	private Lamp  currentLamp;
	 
	public LampController(){
		this.currentLamp = Lamp.S2N;
		this.currentLamp.light();
		ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
		
		timer.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+"   切换方向!---------------------");
				currentLamp = currentLamp.blackOut();
			}
		}, 
		10, 
		10, //每过10秒进行通行方向的切换
		TimeUnit.SECONDS);
	}
}

4、客户端1

public class Main {
	
	public static void main(String[] args) {
		String [] directions = new String[]{
				"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();
	}
}


 

5、客户端2

public class Main {
	
	public static void main(String[] args) {
		String [] directions = new String[]{
				"S2N","S2W","E2W","E2S",
				"N2S","N2E","W2E","W2N",
				"S2E","E2N","N2W" ,"W2S"
		};
		
		for (int i=0;i<directions.length;i++){
			new Road(directions[i]);
		}
		//开启3个线程池		
		new LampController();
		new LampController();
		new LampController();
	}
}

6、客户端3

public class Main {
	
	public static void main(String[] args) {
		String [] directions = new String[]{
				"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();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		new LampController();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		new LampController();
	}
}



7、客户端1测试结果

N2S_1 is travaling 
N2W_1 is travaling 
E2N_1 is travaling 
N2W_2 is travaling 
W2S_1 is travaling 
E2N_2 is travaling 
S2N_1 is travaling 
S2E_1 is travaling 
E2N_3 is travaling 
N2W_3 is travaling 
S2E_2 is travaling 
pool-25-thread-1   切换方向!---------------------
S2W_1 is travaling 
N2E_1 is travaling 
S2E_3 is travaling 
S2W_2 is travaling 
N2W_4 is travaling 
S2W_3 is travaling 
S2E_4 is travaling 
W2S_2 is travaling 
S2E_5 is travaling 
E2N_4 is travaling 
W2S_3 is travaling 
E2N_5 is travaling 
N2W_5 is travaling 
S2W_4 is travaling 
W2S_4 is travaling 
N2E_2 is travaling 
E2N_6 is travaling 
S2W_5 is travaling 
E2N_7 is travaling 
S2W_6 is travaling 
pool-25-thread-1   切换方向!---------------------
E2W_1 is travaling 

 

8、客户端2测试结果

W2S_1 is travaling 
N2S_1 is travaling 
N2S_2 is travaling 
N2W_1 is travaling 
N2S_3 is travaling 
N2S_4 is travaling 
S2E_1 is travaling 
W2S_2 is travaling 
N2S_5 is travaling 
pool-26-thread-1   切换方向!---------------------
pool-25-thread-1   切换方向!---------------------
pool-27-thread-1   切换方向!---------------------
S2W_1 is travaling 
N2E_1 is travaling 
E2N_1 is travaling 
E2N_2 is travaling 
N2W_2 is travaling 
S2E_2 is travaling 
N2E_2 is travaling 
W2S_3 is travaling 
S2W_2 is travaling 
E2N_3 is travaling 
S2W_3 is travaling 
S2E_3 is travaling 
N2W_3 is travaling 
S2W_4 is travaling 
S2E_4 is travaling 
W2S_4 is travaling 
pool-25-thread-1   切换方向!---------------------
pool-26-thread-1   切换方向!---------------------
pool-27-thread-1   切换方向!---------------------
E2W_1 is travaling 
W2E_1 is travaling 
E2W_2 is travaling 
W2E_2 is travaling 
N2W_4 is travaling 
W2S_5 is travaling 
E2W_3 is travaling 
W2E_3 is travaling 
W2E_4 is travaling 
S2E_5 is travaling 
N2W_5 is travaling 
W2E_5 is travaling 
W2S_6 is travaling 
E2N_4 is travaling 
E2W_4 is travaling 
W2E_6 is travaling 
pool-25-thread-1   切换方向!---------------------
pool-26-thread-1   切换方向!---------------------
pool-27-thread-1   切换方向!---------------------
E2S_1 is travaling 
W2N_1 is travaling 

9、客户端3测试结果

N2S_1 is travaling 
E2N_1 is travaling 
N2S_2 is travaling 
W2S_1 is travaling 
S2N_1 is travaling 
S2E_1 is travaling 
E2N_2 is travaling 
N2W_1 is travaling 
N2S_3 is travaling 
pool-25-thread-1   切换方向!---------------------
S2W_1 is travaling 
N2E_1 is travaling 
S2E_2 is travaling 
W2S_2 is travaling 
pool-26-thread-1   切换方向!---------------------
S2W_2 is travaling 
pool-27-thread-1   切换方向!---------------------
S2W_3 is travaling 
N2E_2 is travaling 
E2N_3 is travaling 
N2E_3 is travaling 
W2S_3 is travaling 
N2W_2 is travaling 
E2N_4 is travaling 
pool-25-thread-1   切换方向!---------------------
E2W_1 is travaling 
W2E_1 is travaling 
S2E_3 is travaling 
pool-26-thread-1   切换方向!---------------------
E2W_2 is travaling 
W2E_2 is travaling 
pool-27-thread-1   切换方向!---------------------
E2W_3 is travaling 
W2E_3 is travaling 
S2E_4 is travaling 
W2S_4 is travaling 
E2W_4 is travaling 

四、测试分

1、车辆的产生间隔是随机,因此导致每一个循环通过的车辆数量也是不定的。在客户端只开一个线程池的时候不会涉及到临界资源的同步问题,因此类Lamp的isLighted()方法返回的值不涉及到同步问题。

 public boolean isLighted(){
 	return lighted;
 }

 

2、在客户端开多个线程池的时候Lamp的lighted就成了临界资源,

private boolean lighted ;//亮灯就是 绿


涉及到数据一致性的问题,因此在多线程的情况下应该给isLighted()加上读锁:

public void light(){//灯变绿
		ReentrantReadWriteLock readWriterLock = new ReentrantReadWriteLock(true);  
		readWriterLock.writeLock().lock();//加上读锁
		this.lighted = true;
		readWriterLock.writeLock().unlock();
		if (opposite != null){//相对的灯也要变绿
			Lamp.valueOf(opposite).light();
		}
	}
	public Lamp blackOut(){
		ReentrantReadWriteLock readWriterLock = new ReentrantReadWriteLock(true);  
		readWriterLock.writeLock().lock();//加上读锁
		this.lighted = false;
		readWriterLock.writeLock().unlock();
		if (opposite != null){
			Lamp.valueOf(opposite).blackOut();
			
		}
		Lamp nextLamp = null;
		if (next != null){
			nextLamp = Lamp.valueOf(next);
			nextLamp.light();
		}
		return nextLamp;
	}


3、这个面试题充分体现了面向对象编程的思想,将属性和操作属性的方法封装成一个对象,充分体现了封装隔离的思想。

 

 

 

 


------- android培训java培训、期待与您交流! ----------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值