Java多线程编程作业总结

一.多线程知识总结

1.线程同步

  有关创建线程的知识就不过多的叙述了。就从主要的开始讲吧,讲一下线程的同步。与操作系统中的进程同步一样,线程同样面临着资源共享的问题,怎样处理线程的资源共享是运用多线程最重要的地方。在Java中是引入锁这一概念来处理多线程之间的资源竞争的关系的。“锁”的对象可以是代码块,方法,还可以是对象。一旦某一部分被锁住,我们称该部分获取了锁。那么在java多个线程中,只有拥有锁的线程才可以运行,其他线程不能运行。如果我们将竞争资源的代码块锁起来,就可以避免发生冲突。在运用锁的过程中,通常使用的是synchronized();表示锁住某一部分代码块。那么我们还介绍以下,wait(),和notifyAll(), notify的用法。举个例子,以多线程电梯为例子吧,在多线程电梯中的输入与1调度器之间的关系。我们一次输入同一时刻的多条指令,我们将这多条指令称为临时队列。每次调度每一次输入的指令,将每一次输入的指令加入到总的队列中去。那么就要求输入的时候,不可以运行调度器访问输入的临时队列,因为如果多次访问将造成一次输入被调度多次,而调度的时候也不能写入指令,因为多次写入会导致重写前一次的输入,也就忽略了前一次的输入。看一下代码:

while(true) {
            while(this.sign!=0) {   //表示队列不为空
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            /**
                表示中间处理过程
           */
            sign=1; //用于标记
            notifyAll();
        }    

二.三次作业分析

2.多线程电梯

  刚刚接触多线程确实是十分的艰难,刚拿到的时候毫无头绪。最后的成绩也不是很好,不过之后debug之后感觉就好多了。首先看一下JProfiler性能分析的结果。

JProfiler性能分析的结果

用wait()方法锁住的线程表示为红色,在输入阶段CPU的使用也是最多的,然后是三个电梯都接收指令后开始调度。最后输入END后输入线程结束,还有电梯没有执行完。直到执行完后结束。分为三个阶段的化输入的时候也会有调度,所以处理器的使用率最高,然后是三部电梯同时调度,最后是一部电梯调度,可以看出三部电梯同时调度的时候CPU的使用要比单个电梯调度的时候高很多。说明除了运行线程,CPU处理线程之间的调度也占用CPU。

类图关系

  1. 线程类:Elevator_run; Input; Super_ride。其中Input表示输入线程类,然后是Elevator_run表示电梯运行线程类,所以创建三个Elevator_run线程类,然后是Super_ride线程类,表示调度线程类。线程之间是并行的,但是输入与调度之间的线程必须有wait()和notify()关系,防止对w_legal类中的数据冲突而产生错误。同时,Super_ride类和Elevator_run类之间共享资源Elevator类,所以在Super_ride调用Elevator时Elevator_run不能调用,同理,Elevator_run调用Elevator时Super_ride不能调用,所以在这两个线程中使用synchronized(),实现线程同步。
  2. 关于电梯的类,我将电梯类单独作为一个类,只包括电梯的属性和一个方法,然后将电梯的运行单独作为一个类,Elevator_move类,改类包含所有电梯的方法,以及时间的计算。在Elevator_run线程中通过构造方法传入Elevator类,并新构建一个Elevator_move类的对象。
  3. 关于调度类,调度的方法并不在Super_ride中直接实现,同样我也写了一个专门负责调度的类,只包括调度的属性以及方法。调度的时候首先在Super_ride中通过构造方法传入电梯与指令,然后实例化一个调度器,将电梯与临时队列传入调度器,就可以得到每个电梯的队列。

时序图分析

度量分析

3.文件系统

有关IFTTT

https://en.wikipedia.org/wiki/IFTTT,或者,https://baike.baidu.com/item/ifttt/8378533?fr=aladdin,而IFTTT本事也是一款十分强大的app,大家可以去试试,https://ifttt.com/products

JProfiler性能分析的结果

由于对于这次的测试,测试文件不是很多,所以CPU的使用比较低。从图中可以明显的看出我存在的一个问题,那就是没有一直监控文件。再之后我会说明。一个输入会增加一个线程,也就是说,每一个文件对应一个监控线程,用来实时监控改文件。对于为什么会有线程结束,是因为,再我的程序中,监控文件只会监控一次变化,但是监控目录会监控多次变化。在类图部分会说明原因。开始阶段CPU的使用增长主要时输入的时候需要创建线程的原因。

类图说明

这次的类图明显比较乱

  1. 关于线程类,主要有Modified,Path_changed,Renamed,Size_changed,Test_thread五个线程类。前四个线程不会开始就启动,只有输入有效监控对象,以及监控过程才会新建相应的监控线程。然后是Test_thread是用于测试的线程。
  2. 关于监控文件的类,监控文件的时候,如果是监控目录,那么目录下每一个文件改变都会触发监控器。而目录下文件改变后还要接着监控,我实现的方式是,首先构建一个File_all类;

    class File_all {protected String name=null;
        protected long f_l=0;
        protected long l_t=0;
        protected String path=null;
    }

    由于File方法返回的是指向文件的指针,那么当路径改变的时候,返回值就会改变,所以,File_all的作用是存入路径改变之前的文件的属性,用于之后的文件改变后用于比较。那么是如何获取整个目录下所有的文件的呢?当然是使用递归搜索的方法。

    class Test {
    
        protected void test(String fileDir, ArrayList<File_all> fileList) {
            // = new ArrayList<File_all>();
            File file = new File(fileDir);
    
            File[] files = file.listFiles();// 获取目录下的所有文件或文件夹
            if (files == null) {// 如果目录为空,直接退出
                return ;
            }
            // 遍历,目录下的所有文件,将文件属性存起来
            for (File f : files) {
                File_all tem_file = new File_all();
                if (f.isFile()) {
                    tem_file.file = f;
                    tem_file.name = f.getName();
                    tem_file.f_l = f.length();
                    tem_file.l_t = f.lastModified();
                    fileList.add(tem_file);
                } else if (f.isDirectory()) {
                    test(f.getAbsolutePath(),fileList);
                }
            }
            return ;
        }
    }
  3. 我们将File_all的一个ArrayList传入方法test中,然后就可以调用该方法,获得fileList、也就是存储了所有文件属性的动态数组。
  4. 那么如果两个线程都监控同一文件会不会有资源竞争的问题呢?答案是会的,因为假设线程1是监控A路径下的文件,监控的是Renamed Then recover。线程2监控的也是A路径下的文件,监控是Modified Then details,那么入股哦线程1先运行,那么在线程1还没有recover A路径下的文件的时候,线程2将会得到错误的结果,发现没有文件。我我采用的解决的方法是通过锁住文件的方法,但是只会锁住SIze_changed,和Renamed两个线程中文件的改变,这样监控同一文件的时候,如果有Renamed的情况就会在操作执行完之后才会运行接下来的线程。
  5. 快照的实现,没有仔细阅读指导书,完全按自己想法来,没有快照的概念,只知道需要比较前后两次文件的差异,比较的方法,伪代码如下:

    if(file_2.isDirectory()) {
                chose = 0;
                ArrayList<File_all> FileList = new ArrayList<File_all>();  //新建FileList存储旧的文件
                test_sc.test(obj_path,FileList);   //递归获取旧的文件属性,并存储
                while(true) {
                    ArrayList<File_all> FileList2 = new ArrayList<File_all>();  //新建新文件
                    test_sc.test(obj_path,FileList2);   //递归获取新的文件属性,并存储
                    /**
                    各种操作
                    */
                    FileList.clear();   //各种比较操作结束后,也就是将新旧文件比较后,清空新旧文件
                    FileList2.clear();
                    test_sc.test(obj_path,FileList);    //再一次获得旧文件,存储旧文件,用于之后比较
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

时序图分析

度量分析

度量分析的结果显示,代码还是有漏洞。

4.出租车

JProfiler性能分析的结果

出租车的测试,我采用了一次性输入了大量的请求。从CPU运行的几个峰值来看,第一个是程序开始,创建100个出租车线程,可以看出创建线程是比较占用CPU的,然后是每一次输入,每一次输入是比较占用CPU的,原因是,每一个输入或构建一个调度线程,同样需要创建多个线程,就导致CPU的运行大大增加。中间会有一些较小的峰值,是出租车的运行。

类图分析

  1. 线程类:其实只有Scheduler和Taxi_move,而其中的Input是之前写的,程序并没有用到,忘记改了,也没有删除,然后就被报了一个设计缺陷,…………,这是提交时候的类图,所以Input还在Taxi_move线程是表示出租车运行的线程,100辆出租车也就有100个Taxi_move线程,Taxi_move线程主要是出租车的运行。而Scheduler线程是调度器线程,该线程的创建时在Request类中,每一个有效输入就创建一个调度线程,在3s之内,如果有车接应,那么就完成一次调度,然后通过改变出租车的属性,Taxi_move线程就会让出租车按照一定的方式运行起来。所以出租车是Taxi_move线程和Scheduler线程的竞争资源?也不完全是,因为,调度器不会调度运行状态的车,而Taxi_move只会调度运行状态的车。
  2. 调用GUI,关于怎样将出租车显示在GUI上呢?当然是每次出租车状态改变的时候就刷新出租车的位置啦。
  3. 关于最短路径,与出租车在游荡的时候的随机路径,都在出租车类里面。GUI.java中提供了查找最短路径的方法,也许有部分同学会使用guiInfo类中的distance方法,但是仔细以看,其中D[][],是guiInfo类的一个属性,那么我们就可以直接通过pointbfs(root)方法,获取任意一点到root的最短路径,然后将路径存在D[][]中,这样会大大的减少最短路径的计算时间。采用真实时间的同学,可以这样做,不过在下用的假时间就不必太在乎了。关于随机路径,

    while(true) {
                direction = (int)(1+Math.random()*5);   //随机选一个位置
                if(direction==1 && this.location_x<79 && map_mess.graph[location][location_1]==1) {
                    this.location_x = this.location_x+1;
                    break;
                }
                else if(direction==2 && this.location_x>0 && map_mess.graph[location][location_2]==1) {
                    this.location_x = this.location_x-1;
                    break;
                }
                else if(direction==3 && this.location_y>0 && map_mess.graph[location][location_3]==1) {
                    this.location_y = this.location_y-1;
                    break;
                }
                else if(direction==4 && this.location_y<79 && map_mess.graph[location][location_4]==1) {
                    this.location_y = this.location_y+1;
                    break;
                }
            }

    由于是连通图,所以总会有路走的。

时序图分析

可以看出各个类之间的关系

度量分析

第一次圈复杂度过关了,是不是很高兴,但是这其实是假的。经过多次测试,我发现问题,真实情况是这样的:

然而原因是,我太着急了,第一张图是还没有完全测试完的情况还没有完全显示出圈复杂度。

三.第二次作业总结

关于多线程编程的几点思考

  1. 线程同步:不知道我这种想法对不对,但是从多线程电梯开始,我就是这么想的,一切的资源竞争关系都是生产者与消费者之间的关系。当然也会有同一个类同时扮演两种角色,这时分析起来会比较复杂。
  2. 线程的运行结束,在多线程电梯那里,我就没有处理好电梯的线程的结束问题,因为我们很多情况下用的是while(true)来运行线程,就容易导致这一点,所以必要的标记还是十分重要的。

心得体会

  老实说,我其实每次作业都是抱着,只要有效就行的想法写的,所以最后写的也不是特别好,这三周真的是十分的艰难啊。第一周的时候想到一句话:“No Pains ,No Gains” 。再咬咬牙吧,第二周的时候又想到一句话“如果有一天,你觉得生活如此的艰难,那也许是这次的收获将十分的巨大”。第二周的时候,第三次作业都快想放弃了,生命要紧啊,后来还是咬咬牙坚持了下来,因为又想到一句话”将来的你,一定会感谢现在如此努力的自己“。在这十分艰难的日子里,我也只能靠这些鸡汤度日了。所以想和大家共勉吧,来 ”干了这碗鸡汤“。

原文地址:https://www.cnblogs.com/wevolf/p/8968897.html

时间: 2024-10-03 13:46:06

Java多线程编程作业总结的相关文章

《Java多线程编程核心技术》推荐

写这篇博客主要是给猿友们推荐一本书<Java多线程编程核心技术>. 之所以要推荐它,主要因为这本书写得十分通俗易懂,以实例贯穿整本书,使得原本抽象的概念,理解起来不再抽象. 只要你有一点点Java基础,你就可以尝试去阅读它,相信定会收获甚大! 博主之前网上找了很久都没完整pdf电子版的,只有不全的试读版,这里博主提供免费.清晰.完整版供各位猿友下载: http://download.csdn.net/detail/u013142781/9452683 刚刚已经提到,<Java多线程编程核

Java多线程编程模式实战指南(二):Immutable Object模式--转载

本文由本人首次发布在infoq中文站上:http://www.infoq.com/cn/articles/java-multithreaded-programming-mode-immutable-object.转载请注明作者: 黄文海 出处:http://viscent.iteye.com. 多线程共享变量的情况下,为了保证数据一致性,往往需要对这些变量的访问进行加锁.而锁本身又会带来一些问题和开销.Immutable Object模式使得我们可以在不使用锁的情况下,既保证共享变量访问的线程安

java多线程编程

一.多线程的优缺点 多线程的优点: 1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快 多线程的代价: 1)设计更复杂虽然有一些多线程应用程序比单线程的应用程序要简单,但其他的一般都更复杂.在多线程访问共享数据的时候,这部分代码需要特别的注意.线程之间的交互往往非常复杂.不正确的线程同步产生的错误非常难以被发现,并且重现以修复. 2)上下文切换的开销当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指

Java 多线程编程两个简单的例子

/** * @author gao */ package gao.org; public class RunnableDemo implements Runnable{ @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<10;i++){ System.out.println("新线程输出:"+i); } } public static void main(String []

Java多线程编程基础之线程对象

在进入java平台的线程对象之前,基于基础篇(一)的一些问题,我先插入两个基本概念. [线程的并发与并行] 在单CPU系统中,系统调度在某一时刻只能让一个线程运行,虽然这种调试机制有多种形式(大多数是时间片轮巡为主),但无论如何,要通过不断切换需要运行的线程让其运行的方式就叫并发(concurrent).而在多CPU系统中,可以让两个以上的线程同时运行,这种可以同时让两个以上线程同时运行的方式叫做并行(parallel). 在上面包括以后的所有论述中,请各位朋友谅解,我无法用最准确的词语来定义储

拨开云雾见天日 —— Java多线程编程概念剖析

说到Java多线程编程,大多数人都会想到继承Thread或实现Runnable编程,new 一个Thread实例,调用start()方法,由OS调用即可.具体过程如下: public class MyThread extends Thread {     @Override     public void run() {         System.out.println("MyThread");     }     public static void main(String[] 

java多线程编程中实现Runnable接口方法相对于继承Thread方法的优势

 java多线程创建方法http://blog.csdn.net/cjc211322/article/details/24999163  java创建多线程方法之间的区别http://blog.csdn.net/cjc211322/article/details/25000449 java多线程编程中实现Runnable接口方法相对于继承Thread方法的优势

Java多线程编程(学习笔记)

一.说明 周末抽空重新学习了下多线程,为了方便以后查阅,写下学习笔记. 有效利用多线程的关键是理解程序是并发执行而不是串行执行的.例如:程序中有两个子系统需要并发执行,这时候需要利用多线程编程. 通过多线程的使用,可以编写出非常高效的程序.但如果创建了太多的线程,程序执行的效率反而会降低. 同时上下文的切换开销也很重要,如果创建太多的线程,CPU花费在上下文的切换时间将对于执行程序的时间. 二.Java多线程编程 概念 在学习多线程时,我们应该首先明白另外一个概念. 进程:是计算机中的程序关于某

Java多线程编程详解

线程的同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问. 由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synch