从第五次作业开始,我们接触到了多线程程序设计。多线程能够提高程序运行的效率,但是也对编写代码时数据的同步提出了更高的要求。接下来我将在这里对第五到七次作业进行一个简单的总结。
设计策略
第五次作业中,我们需要将之前的单部电梯拓展为多线程电梯。这次作业主要的数据同步问题在于请求发生器和调度器对于请求队列的数据共享,我采用的方法是使用 BlockingQueue 来代替之前使用的线程不安全的队列,以实现安全的线程间数据共享。第六次作业中,我们需要实现一个线程安全的文件类,这一次作业我主要使用了 Lock 类来给线程加锁,以防止多个线程同时修改一个文件时产生冲突。第七次作业中关于线程安全的部分则与第五次作业类似,只要用 BlockingQueue 来安全地管理请求队列即可。
基于度量分析程序结构
第五次作业
类图
度量
Method metrics | |||
method | ev(G) | iv(G) | v(G) |
multielevator.Elevator.addRequest(ElevatorRequest) | 1.0 | 2.0 | 2.0 |
multielevator.Elevator.carriable(Request) | 2.0 | 11.0 | 11.0 |
multielevator.Elevator.Elevator() | 1.0 | 1.0 | 1.0 |
multielevator.Elevator.Elevator(int,Floor) | 1.0 | 2.0 | 2.0 |
multielevator.Elevator.getFloor() | 1.0 | 1.0 | 1.0 |
multielevator.Elevator.getRunAmount() | 1.0 | 1.0 | 1.0 |
multielevator.Elevator.getState() | 1.0 | 1.0 | 1.0 |
multielevator.Elevator.getTime() | 1.0 | 1.0 | 1.0 |
multielevator.Elevator.move(int) | 1.0 | 1.0 | 6.0 |
multielevator.Elevator.run() | 10.0 | 40.0 | 53.0 |
multielevator.Elevator.setFloor(Floor) | 1.0 | 1.0 | 1.0 |
multielevator.Elevator.toString() | 1.0 | 1.0 | 1.0 |
multielevator.Elevator.waitUntil(long) | 1.0 | 1.0 | 1.0 |
multielevator.ElevatorRequest.ElevatorRequest() | 1.0 | 1.0 | 1.0 |
multielevator.ElevatorRequest.ElevatorRequest(int,int,int,String) | 1.0 | 1.0 | 1.0 |
multielevator.ElevatorRequest.getFloor() | 1.0 | 1.0 | 1.0 |
multielevator.ElevatorRequest.sameAs(Request) | 2.0 | 2.0 | 3.0 |
multielevator.FenwickTree.add(int,int,long) | 1.0 | 3.0 | 3.0 |
multielevator.FenwickTree.add(int,long) | 1.0 | 1.0 | 2.0 |
multielevator.FenwickTree.FenwickTree() | 1.0 | 1.0 | 1.0 |
multielevator.FenwickTree.FenwickTree(long[]) | 1.0 | 2.0 | 2.0 |
multielevator.FenwickTree.getSize() | 1.0 | 1.0 | 1.0 |
multielevator.FenwickTree.query(int) | 1.0 | 1.0 | 2.0 |
multielevator.Floor.addRequest(FloorRequest) | 1.0 | 4.0 | 4.0 |
multielevator.Floor.allocateElevator(FloorRequest) | 1.0 | 8.0 | 10.0 |
multielevator.Floor.Floor(Elevator[]) | 1.0 | 2.0 | 2.0 |
multielevator.Floor.peekRequest(State,int) | 2.0 | 2.0 | 2.0 |
multielevator.Floor.pollRequest(State,int) | 2.0 | 2.0 | 2.0 |
multielevator.Floor.run() | 4.0 | 12.0 | 14.0 |
multielevator.FloorRequest.FloorRequest() | 1.0 | 1.0 | 1.0 |
multielevator.FloorRequest.FloorRequest(State,int,int,String) | 1.0 | 1.0 | 1.0 |
multielevator.FloorRequest.getDirection() | 1.0 | 1.0 | 1.0 |
multielevator.FloorRequest.getFloor() | 1.0 | 1.0 | 1.0 |
multielevator.FloorRequest.oldGetDirection() | 1.0 | 1.0 | 2.0 |
multielevator.FloorRequest.sameAs(Request) | 2.0 | 1.0 | 3.0 |
multielevator.InputProcessor.getBeginTime() | 1.0 | 1.0 | 1.0 |
multielevator.InputProcessor.InputProcessor() | 1.0 | 1.0 | 1.0 |
multielevator.InputProcessor.InputProcessor(String,int) | 1.0 | 1.0 | 1.0 |
multielevator.InputProcessor.printIllegalRequest(String,long,String) | 1.0 | 1.0 | 3.0 |
multielevator.InputProcessor.process() | 3.0 | 4.0 | 5.0 |
multielevator.InputProcessor.processRequest(String,long) | 16.0 | 16.0 | 20.0 |
multielevator.Main.main(String[]) | 4.0 | 7.0 | 10.0 |
multielevator.MultiRequestHandler.MultiRequestHandler(LinkedBlockingDeque) | 1.0 | 1.0 | 1.0 |
multielevator.MultiRequestHandler.run() | 3.0 | 7.0 | 8.0 |
multielevator.Pair.getFirst() | 1.0 | 1.0 | 1.0 |
multielevator.Pair.getSecond() | 1.0 | 1.0 | 1.0 |
multielevator.Pair.Pair() | 1.0 | 1.0 | 1.0 |
multielevator.Pair.Pair(T,int) | 1.0 | 1.0 | 1.0 |
multielevator.Pair.setFirst(T) | 1.0 | 1.0 | 1.0 |
multielevator.Pair.setSecond(int) | 1.0 | 1.0 | 1.0 |
multielevator.Request.getAns() | 1.0 | 1.0 | 1.0 |
multielevator.Request.getElevator() | 1.0 | 1.0 | 1.0 |
multielevator.Request.getFloor() | 1.0 | 1.0 | 1.0 |
multielevator.Request.getInput() | 1.0 | 1.0 | 1.0 |
multielevator.Request.getOrder() | 1.0 | 1.0 | 1.0 |
multielevator.Request.getTime() | 1.0 | 1.0 | 1.0 |
multielevator.Request.printResult(String) | 1.0 | 1.0 | 3.0 |
multielevator.Request.printSame() | 1.0 | 1.0 | 3.0 |
multielevator.Request.Request() | 1.0 | 1.0 | 1.0 |
multielevator.Request.Request(int,int,String) | 1.0 | 1.0 | 1.0 |
multielevator.Request.sameAs(Request) | 1.0 | 1.0 | 1.0 |
multielevator.Request.setAns(String) | 1.0 | 1.0 | 1.0 |
multielevator.Request.setElevator(int) | 1.0 | 1.0 | 1.0 |
multielevator.RequestHandler.getRequests() | 1.0 | 1.0 | 1.0 |
multielevator.RequestHandler.handle() | 1.0 | 8.0 | 8.0 |
multielevator.RequestHandler.move(Request,Elevator) | 1.0 | 1.0 | 1.0 |
multielevator.RequestHandler.RequestHandler() | 1.0 | 1.0 | 1.0 |
multielevator.RequestHandler.RequestHandler(Vector) | 1.0 | 1.0 | 1.0 |
multielevator.RequestQueue.clear() | 1.0 | 1.0 | 1.0 |
multielevator.RequestQueue.front() | 1.0 | 1.0 | 1.0 |
multielevator.RequestQueue.isEmpty() | 1.0 | 1.0 | 1.0 |
multielevator.RequestQueue.pop() | 1.0 | 1.0 | 1.0 |
multielevator.RequestQueue.push(Request) | 3.0 | 3.0 | 3.0 |
multielevator.RequestQueue.RequestQueue() | 1.0 | 1.0 | 1.0 |
multielevator.SmartRequestHandler.checkSame(UnsafeQueue<pair>,int,FenwickTree,Request) | 4.0 | 6.0 | 6.0 |
multielevator.SmartRequestHandler.handle() | 12.0 | 35.0 | 48.0 |
multielevator.SmartRequestHandler.isOnWay(int,int,int,Request) | 2.0 | 4.0 | 7.0 |
multielevator.SmartRequestHandler.modifyTimes(boolean,FenwickTree,int,int) | 1.0 | 7.0 | 7.0 |
multielevator.SmartRequestHandler.SmartRequestHandler() | 1.0 | 1.0 | 1.0 |
multielevator.SmartRequestHandler.SmartRequestHandler(Vector) | 1.0 | 1.0 | 1.0 |
multielevator.UnsafeQueue.clear() | 1.0 | 1.0 | 1.0 |
multielevator.UnsafeQueue.elementAt(int) | 1.0 | 1.0 | 1.0 |
multielevator.UnsafeQueue.front() | 1.0 | 1.0 | 1.0 |
multielevator.UnsafeQueue.isEmpty() | 1.0 | 1.0 | 1.0 |
multielevator.UnsafeQueue.length() | 1.0 | 1.0 | 1.0 |
multielevator.UnsafeQueue.pop() | 1.0 | 1.0 | 1.0 |
multielevator.UnsafeQueue.push(E) | 1.0 | 2.0 | 2.0 |
multielevator.UnsafeQueue.resize() | 1.0 | 1.0 | 2.0 |
multielevator.UnsafeQueue.UnsafeQueue() | 1.0 | 1.0 | 1.0 |
Total | 145.0 | 256.0 | 316.0 |
Average | 1.6292134831460674 | 2.8764044943820224 | 3.550561797752809 |
Class metrics | ||
class | OCavg | WMC |
multielevator.Elevator | 4.3076923076923075 | 56.0 |
multielevator.ElevatorRequest | 1.25 | 5.0 |
multielevator.FenwickTree | 1.8333333333333333 | 11.0 |
multielevator.Floor | 4.5 | 27.0 |
multielevator.FloorRequest | 1.3333333333333333 | 8.0 |
multielevator.InputProcessor | 4.166666666666667 | 25.0 |
multielevator.Main | 5.0 | 5.0 |
multielevator.MultiRequestHandler | 3.5 | 7.0 |
multielevator.Pair | 1.0 | 6.0 |
multielevator.Request | 1.0 | 13.0 |
multielevator.RequestHandler | 2.0 | 10.0 |
multielevator.RequestQueue | 1.3333333333333333 | 8.0 |
multielevator.SmartRequestHandler | 7.0 | 56.0 |
multielevator.State | 0.0 | |
multielevator.UnsafeQueue | 1.2222222222222223 | 11.0 |
Total | 248.0 | |
Average | 2.7252747252747254 | 16.533333333333335 |
Package metrics | ||
package | v(G)avg | v(G)tot |
multielevator | 3.550561797752809 | 316.0 |
设计原则
电梯类的 run 方法中出现了代码逻辑,今后应当避免这种情况。
第六次作业
类图
度量
Method metrics | |||
method | ev(G) | iv(G) | v(G) |
ifttt.FileAttributes.FileAttributes(SafeFile) | 1.0 | 1.0 | 1.0 |
ifttt.FileAttributes.getLastModified() | 1.0 | 1.0 | 1.0 |
ifttt.FileAttributes.getLength() | 1.0 | 1.0 | 1.0 |
ifttt.FileAttributes.getName() | 1.0 | 1.0 | 1.0 |
ifttt.FileAttributes.getParentPath() | 1.0 | 1.0 | 1.0 |
ifttt.FileAttributes.getPath() | 1.0 | 1.0 | 1.0 |
ifttt.FileAttributes.isDirectory() | 1.0 | 1.0 | 1.0 |
ifttt.FileAttributes.setName(String) | 1.0 | 1.0 | 1.0 |
ifttt.FileAttributes.setParentPath(String) | 1.0 | 1.0 | 1.0 |
ifttt.FileAttributes.setPath(String) | 1.0 | 1.0 | 1.0 |
ifttt.InputProcessor.process(String) | 4.0 | 7.0 | 11.0 |
ifttt.Main.main(String[]) | 7.0 | 9.0 | 13.0 |
ifttt.RecordDetail.handle(FileAttributes,FileAttributes) | 1.0 | 1.0 | 2.0 |
ifttt.RecordSummary.handle(FlipFlop) | 1.0 | 4.0 | 5.0 |
ifttt.SafeFile.createNewFile() | 1.0 | 1.0 | 2.0 |
ifttt.SafeFile.delete() | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.exists() | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.getAbsolutePath() | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.getName() | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.getParentFile() | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.isDirectory() | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.lastModified() | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.length() | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.list() | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.mkdir() | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.move(SafeFile) | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.newSafeFile(SafeFile,String) | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.newSafeFile(String) | 2.0 | 2.0 | 2.0 |
ifttt.SafeFile.renameTo(SafeFile) | 4.0 | 4.0 | 4.0 |
ifttt.SafeFile.SafeFile(String) | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.setLastModified(long) | 1.0 | 1.0 | 1.0 |
ifttt.SafeFile.writeString(String,boolean) | 2.0 | 4.0 | 4.0 |
ifttt.Task.equals(Object) | 3.0 | 3.0 | 5.0 |
ifttt.Task.getFilePath() | 1.0 | 1.0 | 1.0 |
ifttt.Task.getSubFiles(SafeFile) | 1.0 | 3.0 | 3.0 |
ifttt.Task.handleTask(FileAttributes,FileAttributes) | 1.0 | 3.0 | 3.0 |
ifttt.Task.monitorModified() | 5.0 | 8.0 | 8.0 |
ifttt.Task.monitorPathChanged() | 8.0 | 9.0 | 12.0 |
ifttt.Task.monitorRenamed() | 8.0 | 10.0 | 13.0 |
ifttt.Task.monitorSizeChanged() | 5.0 | 10.0 | 10.0 |
ifttt.Task.pathChangedSafeFile(ArrayList,FileAttributes) | 4.0 | 5.0 | 6.0 |
ifttt.Task.renamedSafeFile(ArrayList,FileAttributes) | 3.0 | 3.0 | 4.0 |
ifttt.Task.run() | 1.0 | 4.0 | 4.0 |
ifttt.Task.Task(String,FlipFlop,MonitorTask) | 1.0 | 1.0 | 1.0 |
ifttt.Test.run() | 1.0 | 2.0 | 3.0 |
Total | 88.0 | 118.0 | 140.0 |
Average | 1.9555555555555555 | 2.6222222222222222 | 3.111111111111111 |
Class metrics | ||
class | OCavg | WMC |
ifttt.FileAttributes | 1.0 | 10.0 |
ifttt.FlipFlop | 0.0 | |
ifttt.InputProcessor | 9.0 | 9.0 |
ifttt.Main | 9.0 | 9.0 |
ifttt.MonitorTask | 0.0 | |
ifttt.RecordDetail | 1.0 | 1.0 |
ifttt.RecordSummary | 4.0 | 4.0 |
ifttt.SafeFile | 1.2777777777777777 | 23.0 |
ifttt.Task | 5.25 | 63.0 |
ifttt.Test | 2.0 | 2.0 |
Total | 121.0 | |
Average | 2.688888888888889 | 12.1 |
Package metrics | ||
package | v(G)avg | v(G)tot |
ifttt | 3.111111111111111 | 140.0 |
设计原则
出现了部分重复代码。
第七次作业
类图
度量
Method metrics | |||
method | ev(G) | iv(G) | v(G) |
taxi.Graph.addEdge(Vertex,Vertex) | 1.0 | 1.0 | 1.0 |
taxi.Graph.addVertex(Vertex) | 1.0 | 2.0 | 2.0 |
taxi.Graph.bfs(Vertex) | 4.0 | 3.0 | 4.0 |
taxi.Graph.dfs(Vertex,HashSet) | 2.0 | 2.0 | 3.0 |
taxi.Graph.getMinDistance(Vertex,Vertex) | 1.0 | 3.0 | 3.0 |
taxi.Graph.getNeighbourVertices(Vertex) | 1.0 | 1.0 | 1.0 |
taxi.Graph.getNextVertex(Vertex,Vertex) | 1.0 | 3.0 | 3.0 |
taxi.Graph.Graph() | 1.0 | 1.0 | 1.0 |
taxi.Graph.isConnected() | 2.0 | 2.0 | 2.0 |
taxi.InputProcessor.processMap(String,int,Graph) | 12.0 | 7.0 | 13.0 |
taxi.InputProcessor.ProcessRequest(String,Graph,int) | 5.0 | 1.0 | 11.0 |
taxi.Main.main(String[]) | 8.0 | 13.0 | 14.0 |
taxi.Request.equals(Object) | 4.0 | 3.0 | 6.0 |
taxi.Request.getAvailableTaxis() | 1.0 | 1.0 | 1.0 |
taxi.Request.getRequestTime() | 1.0 | 1.0 | 1.0 |
taxi.Request.getRequestVertex() | 1.0 | 1.0 | 1.0 |
taxi.Request.getTargetVertex() | 1.0 | 1.0 | 1.0 |
taxi.Request.hashCode() | 1.0 | 1.0 | 1.0 |
taxi.Request.printResult(String) | 1.0 | 1.0 | 2.0 |
taxi.Request.Request(Vertex,Vertex,long,int) | 1.0 | 1.0 | 2.0 |
taxi.Request.toString() | 1.0 | 1.0 | 1.0 |
taxi.RequestHandler.addRequest(Request) | 3.0 | 3.0 | 4.0 |
taxi.RequestHandler.RequestHandler(ArrayList,Graph,TaxiGUI) | 1.0 | 1.0 | 1.0 |
taxi.RequestHandler.run() | 1.0 | 2.0 | 2.0 |
taxi.RequestHandler.updateRequests() | 5.0 | 14.0 | 15.0 |
taxi.Taxi.getCredit() | 1.0 | 1.0 | 1.0 |
taxi.Taxi.getId() | 1.0 | 1.0 | 1.0 |
taxi.Taxi.getNowVertex() | 1.0 | 1.0 | 1.0 |
taxi.Taxi.getState() | 1.0 | 1.0 | 1.0 |
taxi.Taxi.getStatusTaxi(State) | 1.0 | 3.0 | 3.0 |
taxi.Taxi.getTaxiStatus() | 1.0 | 1.0 | 1.0 |
taxi.Taxi.increaseCredit(int) | 1.0 | 1.0 | 1.0 |
taxi.Taxi.receiving() | 2.0 | 2.0 | 2.0 |
taxi.Taxi.run() | 2.0 | 3.0 | 6.0 |
taxi.Taxi.servicing() | 2.0 | 2.0 | 2.0 |
taxi.Taxi.setRequest(Request) | 1.0 | 1.0 | 1.0 |
taxi.Taxi.setTaxis(ArrayList) | 1.0 | 1.0 | 1.0 |
taxi.Taxi.sleepAndUpdate(long) | 2.0 | 2.0 | 3.0 |
taxi.Taxi.statusId() | 5.0 | 2.0 | 5.0 |
taxi.Taxi.stopping() | 1.0 | 1.0 | 1.0 |
taxi.Taxi.Taxi(Graph,int,TaxiGUI) | 1.0 | 1.0 | 1.0 |
taxi.Taxi.waiting() | 4.0 | 2.0 | 4.0 |
taxi.TaxiStatus.getCoordinateX() | 1.0 | 1.0 | 1.0 |
taxi.TaxiStatus.getCoordinateY() | 1.0 | 1.0 | 1.0 |
taxi.TaxiStatus.getQueryTime() | 1.0 | 1.0 | 1.0 |
taxi.TaxiStatus.getState() | 1.0 | 1.0 | 1.0 |
taxi.TaxiStatus.TaxiStatus(long,int,int,State) | 1.0 | 1.0 | 1.0 |
taxi.Vertex.equals(Object) | 4.0 | 1.0 | 6.0 |
taxi.Vertex.getNeighbourVertices() | 1.0 | 1.0 | 1.0 |
taxi.Vertex.getX() | 1.0 | 1.0 | 1.0 |
taxi.Vertex.getY() | 1.0 | 1.0 | 1.0 |
taxi.Vertex.hashCode() | 1.0 | 1.0 | 1.0 |
taxi.Vertex.setNeighbourVertex(Vertex) | 1.0 | 1.0 | 1.0 |
taxi.Vertex.Vertex(int,int,Graph) | 1.0 | 1.0 | 1.0 |
Total | 104.0 | 108.0 | 148.0 |
Average | 1.9259259259259258 | 2.0 | 2.740740740740741 |
Class metrics | ||
class | OCavg | WMC |
taxi.Graph | 2.2222222222222223 | 20.0 |
taxi.InputProcessor | 8.5 | 17.0 |
taxi.Main | 12.0 | 12.0 |
taxi.Request | 1.3333333333333333 | 12.0 |
taxi.RequestHandler | 4.75 | 19.0 |
taxi.State | 0.0 | |
taxi.Taxi | 2.0 | 34.0 |
taxi.TaxiStatus | 1.0 | 5.0 |
taxi.Vertex | 1.4285714285714286 | 10.0 |
Total | 129.0 | |
Average | 2.388888888888889 | 14.333333333333334 |
Package metrics | ||
package | v(G)avg | v(G)tot |
taxi | 2.740740740740741 | 148.0 |
设计原则
自我感觉没有问题。
Bug分析
第五次作业主要的 bug 是,对于同一时间输入的请求没有处理好,导致后输入的请求先响应。第六次作业主要的 bug 是,监控目录时,只要检测到一个文件发生变化就退出循环并更新快照,这是一个非常低级的算法错误。第七次作业未被报 bug,但是实际上 map.txt 不存在时,我的代码会出现错误。可以看到,这些 bug 与多线程没有什么关系。
这几次作业我没有发现别人的 bug。在进行测试时,我主要是通过阅读被测者的代码来相应地构造测试数据。
心得体会
完成这三次有关多线程的作业之后,我对 java 的线程机制有了较为深刻的理解,也基本学会了如何写出线程安全的 java 程序。在第七次作业中实现设计原则的时候,我深切地感受到了这些设计原则的重要意义,它能够让我们的代码可读性和可移植性更强,减小代码出错的几率。