OO学习体会与阶段总结(多线程程序)

 前言

  在最近一个月的面向对象编程学习中,咱们进入了编写多线程程序的阶段。线程的建立、调度和信息传递,共享对象的处理,线程安全类的编写,各类有关于线程的操做在必定程度上增长了近三次做业的复杂度与难度,带来了不小的考验。本文经过分析总结近三次做业的完成状况,分享我对与多线程编程的一些看法与体会。算法

 

 


 

做业总结分析

多线程电梯调度

(1)题目简述

  实现具备捎带功能的电梯调度系统,调度电梯数量为3部。编程

(2)程序设计

  • 本系统的大体结构与以前的单线程电梯调度系统相似,主要由输入处理、请求调度、电梯模拟三大部分组成。
  • 根据行为的并发特征,为这三大部分各设计了一类线程来实现其功能。分别为输入监听线程、请求调度线程、电梯线程。
  • 系统中的共享资源主要是请求队列,根据请求队列的类型分红了输入队列与执行队列。两者继承了请求集合类,并将这两个类编写成线程安全类。
  • 根据生产者-消费者模型构造线程交互方法。
  • 具体类图以下:

  • 时序图:数组

(3)程序分析

  • 复杂度度量:

     

  • 分析:
    • 在存储请求队列时采用了数组的数据结构。进行请求队列的增减操做时须要遍历数组,致使了相关方法循环复杂度较高。因为运用了长度变量控制遍历的边界,这些方法不会出现危险。能够考虑使用集合类管理此类数据,以减少代码的复杂性,增长可维护性。
    • 线程类的run方法过于复杂,使得该方法的复杂度异常的高。出现该问题主要因为对于线程类的功能的理解不够到位。应当将线程的行为细化,并根据线程运行时须要执行的不一样操做编写相应的方法以供线程调用。这样能提升线程类的可维护性。
  • 数据统计:

    

 

(4)问题分析

  本次做业的问题主要出如今如下两个方面:安全

  • 捎带请求处理:本次做业没有采用前几回做业处理捎带请求的方法,而是根据电梯的运行将分配到的请求按照到达前后顺序构造队列,电梯按照队列头改变运行状态便可。可是在将新请求插入队列的部分,边界判断出现了错误,致使当电梯中止时新请求的插入位置出错。最终使电梯运行路线出错。
  • 输出格式:对于不合法的输入,输出时的格式不对。更改输出格式能够改正。

  本次做业使第一次编写多线程程序。因为对于每一个线程的运行过程的理解不足,我在最初的程序构架时遇到了比较大的困难。尤为是在共享类中锁的运用以及线程类的run方法的功能的设计上,经历了屡次删除重写的过程。在必定程度上,这致使了我在类的设计、调度算法设计等方面有了很多疏忽。最终使得本次做业的完成效果通常。此外,在线程调度方面,本次做业较好的运用了课内讲的模型。经过此次的锻炼,我对于多线程编程的大致框架有了比较深入的认识,为接下来的学习打下了铺垫。数据结构


 

IFTTT

(1)题目简述

  实现IF-THIS-THEN-THAT的文件监控与操做系统。多线程

(2)程序设计

  • 通过分析,程序大体分为文件快照获取、监控事件触发、监控事件任务执行三部分。并发

  • 根据行为的并发特征,为每一个监控事件开启一个线程,实现监控功能;而且设计一个负责实时输出的线程。
  • 具体类图以下:框架

  • 时序图:学习

(3)程序分析

  • 数据统计:测试

  

  • 复杂度分析:

  

  • 分析:

    • 在构造文件系统的遍历树是采用了一维的线性数据结构,每次获取快照时都须要按深度递归遍历监控范围内的全部文件,致使获取快照的相关方法的复杂度较高。在优化的方面,能够考虑改用集合类中的树结构或者哈希表的方式对监控范围内的文件进行预处理(如编码),减少以后的工做量。此外还能够直接使用现有库中的获取快照的方法。

    • 本次做业对与线程类中的run方法进行了必定的精简。相较于上次做业,复杂度有了必定的下降,但仍然是全部方法中最高的。可能的缘由是我在最初设计时为单一线程分配了过多的功能。就本次做业而言,能够按照触发器的不一样将监控线程进行分化。并且还能够将快照获取功能分离成单一线程,经过一个线程安全类与监控线程传递信息。

(4)问题分析

  本次做业的问题主要出如今文件地址的管理方面。在程序中,每一个文件都使用的是绝对地址格式。绝对地址有多种可识别格式,但经过File类提供的方法从File对象中获取的绝对地址仅有一种格式。因为在先后快照对比是经过字符串比较实现的,因此可能因为格式问题致使错误。将格式同一后能够解决。

  在本次做业中最重要的部分就是文件信息的同步。因为文件操做是线程不安全的,因此在完成做业的过程当中,我用了至关多的时间在设计线程安全类和快照获取功能上,最终得到了不错的效果。但在类功能的设计上还显得有些冗杂,有挺大的改进空间。

 


模拟出租车打车系统

(1)题目简述

  实现100辆出租车的抢单调度系统。

(2)程序设计

  • 交互关系分析:

    • 乘客交互的数据特征:提供请求产生时间、乘客位置信息、目的地位置;返回请求的处理结果。
    • 乘客交互的时间特征:不定时产生乘客请求;请求产生3s后返回处理结果。
    • 出租车交互数据特征:获取出租车的位置信息、服务状态和信用信息;返回其抢单结果和须要服务的乘客位置信息与目的地位置。
    • 出租车交互时间特征:以100ms为周期获取。
  • 对象识别与构造:

    • 须要管理的数据及管理手段:
      1. 乘客的请求:

        • 监听获取乘客发出的请求。
        • 维护接受的请求集合。
      2. 乘客请求的响应窗口:

        • 根据请求的位置与时间创建相应的抢单窗口。
        • 维护相应窗口集合。
      3. 出租车:

        • 维护出租车信息的更新。

        • 维护出租车集合。

  • 识别并发行为:

    • 系统与乘客之间交互相对独立,因为只由控制台输入,使用一个线程设计用于与控制台输入交互。

    • 出租车的行为具备显著的重复模式,而且相对独立,使用一种线程设计,分别描述每一辆出租车行为。

    • 对于获取的乘客请求的处理与分配具备重复性,而且相对独立,使用一种线程设计来实现功能。

    • 信息输出相对独立,使用一个线程实现。

  •  具体类图以下:

  • 时序图:

(3)程序分析

  • 数据统计:

  

  • 复杂度分析:

  

  • 分析:

    • 相较于上次做业,本次做业方法的复杂度有了明显的下降(不考虑现成的GUI代码)。在最初设计的时候,我就考虑到了SOLID等原则,并尽可能使设计的类符合以上的原则,作出了风格上的改变。其中线程类的设计上,尽可能分配更少、更简洁的功能,使其仅需完成线程部分必要的代码。从而使得整个程序的可维护性与可读性有了至关大的提升。

(4)问题分析

  在测试阶段个人程序没有被发现问题。可是在本身检查的时候发现了很多的潜在问题:

  • 地图管理:本次做业我使用了gui中给好的地图管理类,使用矩阵管理地图。每当出租车接单时都须要调用广度遍历方法来获取最短路径。这使得出租车在路径的选择上缺少功能延展性,只能走最初设定好的路线,没法适应路况的变化。
  • 出租车管理:在本次做业中,我为了简单将全部的出租车都管理在了同一个数组中,将出租车状态保存在了出租车各自的对象中。若是能将不一样状态的出租车分离,用不一样的策略去管理,则可以使程序的延展性更高。

 


总结

  通过了这三次的多线程编程做业,经过分析做业中的问题,我对于线程的调度与线程的安全有了至关深刻的认识。从宏观上来看,每一个线程都在同时运行,但微观上看他们是在JVM的调度之下按原子操做交替执行。若是每一个线程间相互独立,JVM调度的不肯定性不会影响程序的可再现性。但就像电梯中的请求队列、IFTTT中的文件信息、出租车系统的请求和出租车信息,每一个线程间难以免地会有共享的数据,此时就须要经过同步、互斥的手段防止JVM调度的不肯定性破坏程序的可再现性。一般,经过锁机制就可以实现这些功能。可是,在共享关系比较复杂的状况下,单纯的使用锁机制并不必定可以达到预期的效果。这时就须要一种模式化的线程安全保护措施。那就是线程安全类。编写线程安全类就比如为已有的功能代码加上一层外皮,其内部代码保证功能的实现,外部接口保证入口的互斥,从而实现线程安全。但这又带来了另外一个问题:同步部分的代码长度对多线程效率的影响。进而对于临界区的功能安排应当尽可能精简,以免对多线程机制的浪费。

  此外,通过了对面向对象思想的学习,我认识到了面向对象程序设计的12大基本原则,而且将其实践到最近的一次做业当中。在亲自编写代码的过程当中,我体会到这些原则最核心的想法就是:设计具备层次性、代码具备可延展性。在设计时就要从最外层的交互设计,一层层深刻,到内部对象的建模、对象间交互,再到类内部的设计。这个设计就是对整个环境与系统的逐层深刻,将各个功能逐层分离,最终造成相似树状的类设计。而在编写代码的时候不该当仅关注于当前的功能实现,更应当想到更多同类型的操做。换句话说就是编写出的方法、类不该当进可以实现特例操做,而更应当面向更为抽象、更为通用的层次上。

相关文章
相关标签/搜索