1、題目要求 java
模擬實現十字路口的交通燈管理系統邏輯,具體需求如: 編程
一、異步隨機生成按照各個路線行駛的車輛。 dom
例如: 異步
由南向而來去往北向的車輛 ----直行車輛 ide
由西向而來去往南向的車輛 ----右轉車輛 函數
由東向而來去往南向的車輛 ----左轉車輛 工具
。。。 this
二、信號燈忽略黃燈,只考慮紅燈和綠燈。 spa
三、應考慮左轉車輛控制信號燈,右轉車輛不受信號燈控制。 線程
四、具體信號燈控制邏輯與現實生活中普通交通燈控制邏輯相同,不考慮特殊狀況下的控制邏輯。
五、注:
1)南北向車輛與東西向車輛交替放行,同方向等待車輛應先放行直行車輛然後放行左轉車輛。
2)每輛車經過路口時間爲1秒(提示:可經過線程Sleep的方式模擬)。
3)隨機生成車輛時間間隔以及紅綠燈交換時間間隔自定,能夠設置。
4)不要求實現GUI,只考慮系統邏輯實現,可經過Log方式展示程序運行結果。
2、題意分析
一、十字路口的圖解分析
分析:
1)各方向行駛車輛總共有 12 條路線。 由圖分析出直行車輛有 4 條路線, 左轉彎車輛有4 條路線,右轉彎車輛有 4 條路線。1)每一個Road對象都有一個name成員變量來表明方向,有一個vehicles(交通工具)成員變量來表明方向上的車輛集合。
2)在Road對象的構造方法中啓動一個線程每隔一個隨機的時間向vehicles集合中增長一輛車(用一個「路線名_id」形式的字符串進行表示)。
3)在Road對象的構造方法中啓動一個定時器,每隔一秒檢查該方向上的燈是否爲綠,是則打印車輛集合和將集合中的第一輛車移除掉。
代碼以下:
package trafficlamp; 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; /** * 每一個Road對象表明一條路線,總共有12條路線,即系統中總共要產生12個Road實例對象。 每條路線上隨機增長新的車輛,增長到一個集合中保存。 * 每條路線每隔一秒都會檢查控制本路線的燈是否爲綠,是則將本路線保存車的集合中的第一輛車移除,即表示車穿過了路口。 * * @author Cavenzep * */ public class Road { // 定義一個集合,用來存儲和操做車輛這個字符串對象,使用List接口是爲了擴展性更強 private List<String> vechicles = new ArrayList<String>(); // 定義路線名變量,用於標識 private String name; // 構造函數 public Road(String name) { this.name = name; /* 模擬車輛不斷隨機上路的過程 */ // 經過產生單個線程的方法,建立一個線程池 ExecutorService pool = Executors.newSingleThreadExecutor(); // 調用execute方法,可向線程池提交一個任務,讓池中的線程執行任務 pool.execute(new Runnable() { @Override // 複寫run方法,須要執行的代碼,隨機產生車輛,並存入集合 public void run() { for (int i = 1; i < 1000; i++) { try { // 10秒內隨機產生一輛車 Thread.sleep((new Random().nextInt(10) + 1) * 1000); } catch (InterruptedException e) { e.printStackTrace(); } // 隨機產生的車輛進入路線中 vechicles.add(Road.this.name + "_" + i); } } }); // 定義一個定時器,每隔一秒檢查對應的燈是否爲綠,是則放行一輛車 ScheduledExecutorService timer = Executors.newScheduledThreadPool(1); timer.scheduleAtFixedRate(new Runnable() { @Override // 定時器要執行的代碼 public void run() { // 判斷該路線中是否有車,有則進行放行操做 if (vechicles.size() > 0) { // 若是該路線上對應的燈是綠色的,則放行車輛 boolean lighted = Lamp.valueOf(Road.this.name).isLighted(); if (lighted) { System.out.println(vechicles.remove(0) + "\tis traveling!"); } } } }, 1,/* 隔多少秒執行 */1,/* 週期 */TimeUnit.SECONDS/* 時間單位 */); } }
二、Lamp類的編寫
1)系統中有12個方向上的燈,在程序的其餘地方要根據燈的名稱就能夠得到對應的燈的實例對象,綜合這些因素,將Lamp類用java5中的枚舉形式定義更爲簡單。
2)每一個Lamp對象中的亮黑狀態用lighted變量表示,選用S2N、S2W、E2W、E2N這四個方向上的Lamp對象依次輪詢變亮,Lamp對象中還要有一個oppositeLampName變量來表示它們相反方向的燈,再用一個nextLampName變量來表示此燈變亮後的下一個變亮的燈。這三個變量用構造方法的形式進行賦值,由於枚舉元素必須在定義以後引用,因此沒法再構造方法中彼此相互引用,因此,相反方向和下一個方向的燈用字符串形式表示。
3)增長讓Lamp變亮和變黑的方法:light和blackOut,對於S2N、S2W、E2W、E2N這四個方向上的Lamp對象,這兩個方法內部要讓相反方向的燈隨之變亮和變黑,blackOut方法還要讓下一個燈變亮。
4)除了S2N、S2W、E2W、E2N這四個方向上的Lamp對象以外,其餘方向上的Lamp對象的nextLampName和oppositeLampName屬性設置爲null便可,而且S2N、S2W、E2W、E2N這四個方向上的Lamp對象的nextLampName和oppositeLampName屬性必須設置爲null,以便防止light和blackOut進入死循環。
代碼:
package trafficlamp; /** * 每一個Lamp元素表明一個方向上的燈,總共有12個方向,全部總共有12個Lamp元素。 * 有以下一些方向上的燈,每兩個造成一組,一組燈同時變綠或變紅,因此, * 程序代碼只須要控制每組燈中的一個燈便可: * s2n,n2s * s2w,n2e * e2w,w2e * e2s,w2n * s2e,n2w * e2n,w2s * 上面最後兩行的燈是虛擬的,因爲從南向東和從西向北、以及它們的對應方向不受紅綠燈的控制, * 因此,能夠假想它們老是綠燈。 * @author Cavenzep */ // S2N,S2W,E2W,E2S,N2S,N2E,W2E,W2N,S2E,E2N,N2W,W2S public enum Lamp { /*每一個枚舉元素各表示一個方向上的控制燈*/ S2N(false,"N2S","S2W"),S2W(false,"N2E","E2W"),E2W(false,"W2E","E2S"),E2S(false,"W2N","S2N"), /*下面元素表示與上面的元素的相反方向的燈,它們的「反方向燈」和「下一個燈」應忽略不計!*/ N2S(false,null,null),N2E(false,null,null),W2E(false,null,null),W2N(false,null,null), /*下面元素表示四個右轉彎方向的燈,由於其不受紅綠燈控制,因此能夠假設它們老是綠燈*/ S2E(true,null,null),E2N(true,null,null),N2W(true,null,null),W2S(true,null,null); //當前燈的狀態,是否爲綠 private boolean lighted; //當前燈變紅時,下個綠的燈 private String next; //與當前燈相反方向的同爲綠的燈 private String opposite; //構造函數 private Lamp(boolean lighted,String opposite,String next){ this.lighted=lighted; this.next=next; this.opposite=opposite; } //提供一個判斷是否爲亮(綠)的方法 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 下面將能看到六個方向的車輛經過。 "); } /** * 某個燈變紅時,對應方向的燈也要變紅,而且下一個方向的燈要變綠 * @return 下一個要變綠的燈 */ 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類的編寫
1)整個系統中只能有一套交通燈控制系統,因此,LampController類最好是設計成單例。
2)LampController構造方法中要設定第一個爲綠的燈。
3)LampController對象的start方法中將當前燈變綠,而後啓動一個定時器,每隔10秒將當前燈變紅和將下一個燈變綠。
代碼:
package trafficlamp; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * 紅綠燈控制系統用來控制紅綠燈的切換時間 每隔10秒將當前燈變紅,並按順序將下一個方向的燈變綠 * * @author Cavenzep */ public class LampController { // 定義當前燈用於第一個綠的燈 private Lamp currentLamp; public LampController() { // 剛開始讓由南向北的燈變綠 currentLamp = Lamp.S2N; currentLamp.light(); // 定義一個定時器,每隔10秒就將當前燈由綠變紅,並將下一個燈變綠 ScheduledExecutorService timer = Executors.newScheduledThreadPool(1); timer.scheduleAtFixedRate(new Runnable() { @Override public void run() { // 將下一個燈切換爲當前的燈 currentLamp = currentLamp.blackOut(); System.out.println("燈變了"); } }, 10, 10, TimeUnit.SECONDS); } }
四、MainClass類的編寫
1)用for循環建立出表明12條路線的對象。
2)建立紅綠燈控制系統對象,啓動系統
代碼:
package trafficlamp; /** * 主程序,用於啓動控制系統,並實現題意過程 * * @author Cavenzep */ public class MainClass { public static void main(String[] args) { /* 產生12個方向的路線 */ String[] directions = new String[] { "S2N", "S2W", "E2W", "E2S", "N2S", "N2E", "W2E", "W2N", "S2E", "E2N", "N2W", "W2S" }; for (String direction : directions) { new Road(direction); } // for (int i = 0; i < directions.length; i++) { // new Road(directions[i]); // } /* 產生整個交通燈系統並運行 */ new LampController(); } }結果以下所示: