前言

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


作业总结分析

多线程电梯调度

(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大基本原则,并且将其实践到最近的一次作业当中。在亲自编写代码的过程中,我体会到这些原则最核心的想法就是:设计具有层次性、代码具有可延展性。在设计时就要从最外层的交互设计,一层层深入,到内部对象的建模、对象间交互,再到类内部的设计。这个设计就是对整个环境与系统的逐层深入,将各个功能逐层分离,最终形成类似树状的类设计。而在编写代码的时候不应当仅关注于当前的功能实现,更应当想到更多同类型的操作。换句话说就是编写出的方法、类不应当进能够实现特例操作,而更应当面向更为抽象、更为通用的层次上。

OO学习体会与阶段总结(多线程程序)的更多相关文章

  1. OO学习体会与阶段总结(设计与实现)

    前言   在最近的一个月的课程中,笔者对于规格化编程进行了深入的学习.运用面向对象抽象思想对编写的程序进行过程抽象.异常处理.数据抽象.类的层次规格与迭代等等规格设计,使得程序结构化程度提高,具有更好 ...

  2. OO学习体会与阶段总结(测试与论证)

    前言   随着期末的到来,对于面向对象程序设计课程的学习也迎来了尾声.在最后一个月的从课程中,笔者对于面向对象程序规格实现层面的单元测试.正确性论证以及使用UML图描述程序的设计进行了深入的学习.通过 ...

  3. Linux学习笔记20——第一个多线程程序

    一 什么是线程 线程:是一个进程内部的一个控制序列. 二 使用POSIX的注意点 1 为了使用线程函数库,必须定义宏_REENTRANT,通过定义_REENTRANT来告诉编译器我们需要可重入功能,可 ...

  4. Java程序猿学习当中各个阶段的建议

    回答阿里社招面试如何准备,顺便谈谈对于Java程序猿学习当中各个阶段的建议   引言 其实本来真的没打算写这篇文章,主要是LZ得记忆力不是很好,不像一些记忆力强的人,面试完以后,几乎能把自己和面试官的 ...

  5. 渐入OO课的深处,探索多线程的秘密——OO第二次博客总结

    一次又一次的挑战,一次又一次全新的知识,我来到了多线程的面前 第五次作业 1.度量分析 >第五次作业由于很大程度上调用的是前两次电梯的一些代码,所以存在的问题与前几次也十分相似.同时由于第一次使 ...

  6. OO第二次单元总结——电梯多线程调度问题

    OO第二次单元总结--电梯多线程调度问题 在这个单元OO学习中,我们终于迎来了期待已久(不是)的电梯多线程调度作业,开启了OO打怪之路的新关卡.虽然说经过了这三次作业,我对于多线程的理解还不能算是熟练 ...

  7. Java多线程学习---------超详细总结(java 多线程 同步 数据传递 )

    目录(?)[-] 一扩展javalangThread类 二实现javalangRunnable接口 三Thread和Runnable的区别 四线程状态转换 五线程调度 六常用函数说明 使用方式 为什么 ...

  8. 【OO学习】OO第四单元作业总结及OO课程总结

    [OO学习]OO第四单元作业总结及OO课程总结 第四单元作业架构设计 第十三次作业 第十四次作业 总结 这两次作业架构思路上是一样的. 通过将需要使用的UmlElement,封装成Element的子类 ...

  9. Linux多线程实践(10) --使用 C++11 编写 Linux 多线程程序

    在这个多核时代,如何充分利用每个 CPU 内核是一个绕不开的话题,从需要为成千上万的用户同时提供服务的服务端应用程序,到需要同时打开十几个页面,每个页面都有几十上百个链接的 web 浏览器应用程序,从 ...

随机推荐

  1. python代码打包发布

    背景 本文介绍了python中一种最简单的代码结构的打包方式 包名称 我们先给我们的包取个名字,python包起名需要符合下面的规范 全部小写 在pypi上是唯一的 下划线分隔或没有单词分隔符(不要使 ...

  2. MapReduce:Shuffle过程详解

    1.Map任务处理 1.1 读取HDFS中的文件.每一行解析成一个<k,v>.每一个键值对调用一次map函数.                <0,hello you>   & ...

  3. 报表使用hive数据源报java.net.SocketTimeoutException: Read timed out

    数据库表的数据量大概50W左右,在报表设计器下创建了hive的数据源,连接正常,由于数据量比较大,就用了润乾报表的大数据报表功能,报表设置好后,发布到页面中报错: 数据集ds1中,SQL语句SELEC ...

  4. 9.Java注解(Annotation)

    一.系统内置标准注解 1.@Override 是一个标记注解类型,它被用作标注方法. 它说明了被标注的方法重载了父类的方法,起到了断言的作用.如果我们使用了这种Annotation在一个没有覆盖父类方 ...

  5. ubuntu 常见配置文件

    环境变量:linux的环境变量其实就是在启动时执行一下赋值语句 系统级 1./etc/environment   系统级$PATH   2.其他启动文件(https://www.cnblogs.com ...

  6. linux something

    使用的UbuntuKylin  source error e1:apt-get update提示没有证书 e2:访问不了阿里云源服务器 e3:GPG 错误:http://download.mono-p ...

  7. 零基础图文傻瓜教程接入Facebook的sdk

    零基础图文傻瓜教程接入Facebook的sdk 本人视频教程系类   iOS中CALayer的使用 0. 先解决你的 VPN FQ上外网问题,亲,解决这一步才能进行后续操作^_^. 1. 点击右侧链接 ...

  8. Linux env命令详解

    env:查询环境变量 常用的命令展示 查看当前环境的环境变量 [root@localhost ~]# env HOSTNAME=localhost.localdomain SELINUX_ROLE_R ...

  9. javascript unshift()和shift()

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  10. 处理过期的archivelog和rman备份

    当手工删除了归档日志以后,Rman备份会检测到日志缺失,从而无法进一步继续执行.所以此时需要手工执行crosscheck过程,之后Rman备份可以恢复正常.[执行顺序如下:手工删除archivelog ...