黑马程序员——交通灯系统

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

面向对象设计把握一个重要的经验:谁拥有数据,谁就对外提供操作这些数据的方法。
再牢牢记住几个案例就可以了:
人在黑板上画圆——>圆心跟半径都是圆本身的属性,所以应该由圆本身提供此方法
列车司机紧急刹车
售货员统计收获小票的金额——>金额是小票上的属性所以此方法该在小票上
你把门关上
小球从绳子的一段移动到绳子的另一端
两块石头磨成石刀,石刀砍树,做成椅子。
(如果将一个对象变为另一个对象的方法一般不会放在原本对象类中,而用工厂模式
StoneKnife = KnifeFactory.createKnife(stone) )
Stone
StoneKnife
tree
chair = ChairFactory.makeChair(material)
material
chair

JAVA1.5的线程池

ExecutorService pool = Executors.newSingleThreadExecutor();
pool.execute(new Runnable(){public void run(){}});

定时器:就是一种线程控制器。
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate(
new Runnable(){public void run(){}},//需要重复干的事情
1,//过多少秒干第一次
1,//之后隔多少秒
TimeUnit.SECONDS);

枚举的构造方法必须是私有的

 交通灯项目

 

项目需求:

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

1、  异步随机生成按照各个路线行驶的车辆。

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

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

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

      。。。。

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

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

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

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

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

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

7、  不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。

 

总共有12条路线,为了统一编程模型,可以假设每条路线都有一个红绿灯对其进行控制,右转弯的4条路线的控制灯可以假设为常绿状态,另外,其他的8条线路是两两成对的,可以归为4组,所以,程序只需考虑图中标注了数字号的4条路线的控制灯的切换顺序,这4条路线相反方向的路线的控制灯跟随这4条路线切换,不必额外考虑。

 

 

 

我的分析:


一共12条路线,那么运行的时候就会有12个线程不停得给每天路线上new车
还有12个线程(定时器)每一秒remove掉一个路线上的car。
这样就出现了一个问题,一个线程要调用另一个线程的数组,那么就得static而一旦static的话,
那么每个方向上的数组都将同步,这并不是我们想要的。
我们只想两个线程共享一个数组,老师用了一个绝佳的方法解决了此问题,就是在Road类的构造器
中创建出两个线程,既不影响Road对象的创建,又实现了两个线程共享一个数组,还简化了代码量。

那么每隔一段时间交通灯的灯系统都会变化一次,这个也得由一个单独线程控制。

还有怎么实现两两线程的切换呢?那么就得用一个对象的属性作为开关,也就是控制器所用的对象,
那么remove线程中会有一个判断,如果不能运行那么久让他wait,并且notify其他所有线程。
老师解决的方案就是用一个定时器,那就不需要wait跟notify了,真心漂亮。

如果我这么做的话,我必须得手动写4个类实现runnable接口,那将非常累,代码重复量过多,真心劣质。

 

老师仅仅使用了一个枚举类,跟一个控制类,就实现了5组对象的同时、多种控制,高复用明显。
老师的这个小项目,不管我怎么读,都感觉妙不可言,再无可优化。


 代码编写:

Road类:用于随机new出自己线上的车以及使车通过的remove。

此方法的妙处就是:在构造器内部创建两个线程,即不影响构造器的创建,又实现了两个线程同享一个List,非常妙。

 

package com.isoftstone.interview.traffic;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
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> vechicles = new ArrayList<String>();
	
	private String name = null;
	public Road (String name){
		this.name = name;
		// 想实现每条路的不断创建车辆,除了实现runnable接口以外,
		// 还可以直接在构造方法里面再创建一个线程
		// 因为必须先有对象才能调用方法,而这个是永动方法,所以定义在外面然后调用
		// 不如直接用多线程放在构造方法之中。
		// 其实实现runnable接口只能有一个run方法,只能实现new车跟move车中的一个,
		// 如果都需要实现只能再新建一个线程了。
		// 如此看来定义在构造方法中的想法实在是太棒了。
		ExecutorService pool = Executors.newSingleThreadExecutor();
		pool.execute(new Runnable(){
			public void run(){
				for (int i = 1; i <= 1000; i++) {
					try {
						Thread.sleep((new Random().nextInt(10)+1)*1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					// 内部类访问外部类中的成员变量有两种方法:
					// 1.将外部类中的成员变量设为final类型
					// 2.访问的时候格式换为:外部类类名.this.变量名
					vechicles.add(Road.this.name +"_"+i);
				}
			}
		});
		// 每隔一秒remove掉一个
		ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
		timer.scheduleAtFixedRate(new Runnable(){
			public void run(){
				if (vechicles.size()>0) {
					// 灯绿了才让行
					boolean lighted = Lamp.valueOf(Road.this.name).isLighted();
					if (lighted) {
						System.out.println(vechicles.remove(0)+"  is  travaling!");
					}
				}
			}},
			1, 
			1,
			TimeUnit.SECONDS);
	}
}

package com.isoftstone.interview.traffic;

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);
	
	// 如果这里的参数是一个对象,那么就会出现一个不断需要先new对方的死循环,也就是先有鸡还是先有鸡蛋的问题
	// 所以该成同名字符串代替
	private Lamp(String opposite, String next, boolean lighted) {
		this.opposites = opposite;
		this.next = next;
		this.lighted = lighted;
	}

	private boolean lighted;
	private String opposites;
	private String next;

	public boolean isLighted() {
		return lighted;
	}

	public void light() {
		this.lighted = true;
		// 一组对应的灯就只有一个有对应的灯
		if (opposites != null) {
			Lamp.valueOf(opposites).light();
		}
		// name()是枚举的内部方法
		System.out.println(name() + "灯变绿了!");
	}

	public Lamp blackOut() {
		// 将当前灯状态改为红、false
		this.lighted = false;
		// 将对应方向的灯也改为false
		if (opposites != null) {
			Lamp.valueOf(opposites).blackOut();
		}
		// 将下个方向的灯变绿
		Lamp nextLamp = null;
		if (next != null) {
			nextLamp = Lamp.valueOf(next);
			System.out.println("路灯从" + name() + "切换成——————>" + next + "  "
					+ opposites);
			Lamp.valueOf(next).light();
		}
		return nextLamp;
	}
}


 

Lamp类跟LampController控制类

实现了对5类对象多个属性的有序控制,非常伟大。

 

package com.isoftstone.interview.traffic;

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);
	
	// 如果这里的参数是一个对象,那么就会出现一个不断需要先new对方的死循环,也就是先有鸡还是先有鸡蛋的问题
	// 所以该成同名字符串代替
	private Lamp(String opposite, String next, boolean lighted) {
		this.opposites = opposite;
		this.next = next;
		this.lighted = lighted;
	}

	private boolean lighted;
	private String opposites;
	private String next;

	public boolean isLighted() {
		return lighted;
	}

	public void light() {
		this.lighted = true;
		// 一组对应的灯就只有一个有对应的灯
		if (opposites != null) {
			Lamp.valueOf(opposites).light();
		}
		// name()是枚举的内部方法
		System.out.println(name() + "灯变绿了!");
	}

	public Lamp blackOut() {
		// 将当前灯状态改为红、false
		this.lighted = false;
		// 将对应方向的灯也改为false
		if (opposites != null) {
			Lamp.valueOf(opposites).blackOut();
		}
		// 将下个方向的灯变绿
		Lamp nextLamp = null;
		if (next != null) {
			nextLamp = Lamp.valueOf(next);
			System.out.println("路灯从" + name() + "切换成——————>" + next + "  "
					+ opposites);
			Lamp.valueOf(next).light();
		}
		return nextLamp;
	}
}


 

package com.isoftstone.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入口类

package com.isoftstone.interview.traffic;

public class MainClass {
	public static void main(String[] args) {
		String[] directions = new String[]{
				"S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"
		};
		// 运用数组巧妙的创建了12个方向的路。
		for (int i = 0; i < directions.length; i++) {
			new Road(directions[i]);
		}
		// 打开控制器
		new LampController();
	}
}


 

 

 

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

你可能感兴趣的:(多线程,枚举,面向对象,交通,构造器里加双线程)