一、面向对象思想
本例很好的体现了面向对象编程的思想,现实生活中有很多的问题,包括一些有趣的探讨性话题,都可以用面向对象的模型来分析和解决,比如球,船,蚂蚁和木棍,这里的路和车。
在本例中,将路看成一个对象来研究,其中:
固有的属性:name,vehicles ,红绿灯。
固有的动作:路上会新增车辆,车辆会通过路口。
路上的车辆要通过马路,就要遵守一定的交通规则:
1、东南西北四个方向的车辆右拐不需要有红绿灯,每时每刻都可以行使
2、四对方向:( S2N -- N2S) (E2W -- W2E) (S2W -- N2E ) (E2S -- W2N)
3、四对方向需要遵守交通规则,而且同一时间,只有一对方向可以行使
4、交通灯按照 南 - 东 - 北 - 西 的逆时针方向变化。
二、解决方案
1、系统
路:方向,经过以上分析可以得到这个十字路口可以分为12个方向,每个方向上面的车辆都是随机的增加和减少(Daemon线程控制)
车:每个方向上的车辆会随机的增加和减少,可以使用一个容器来容纳
灯:每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口。
2、控制
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培训、期待与您交流! ----------