201621123031 《Java程序设计》第11周学习总结

做业11-多线程


1. 本周学习总结

1.1 以你喜欢的方式(思惟导图或其余)概括总结多线程相关内容。

2. 书面做业

本次PTA做业题集多线程java

1. 源代码阅读:多线程程序BounceThread

1.1 BallRunnable类有什么用?为何代码中须要调用Thread.sleep进行休眠?

答:BallRunnable类实现了Runnable接口,支持了多线程,在其中用于给线程指派任务,在类中的run方法里经过循环调用小球的移动函数move和重画函数repaint来实现小球的移动轨迹。还有用了Treep.sleep()来使线程休眠一段时间。
使用Tread.sleep让线程进行休眠是为了延缓线程完成的时间,这样可让咱们看到小球的移动,否则不进行休眠的话,这个程序会在一个很快的时间内完成,没有办法看到小球的移动轨迹,而且sleep()为Thread类的静态方法,调用它不会建立一个新线程。安全

1.2 Ball.java只作了两件事,这两件事分别是什么?BallComponent对象是干什么的?其内部的ArrayList有什么用?程序运行过程当中,生成了几个BallComponent对象?该程序使用了多线程技术,每一个小球是分别在不一样的线程中进行绘制吗?

答:Ball.java完成的两件事:多线程

  • 定义了move函数实现小球的移动方法
  • 定义了getShape函数来获取小球的大小和坐标

BallComponent对象也实现了两件事:dom

  • 添加一个小球
  • 画出一个小球

其内部的ArrayList用于存放添加的小球
程序中只生成了一个BallComponent对象
经过源代码能够看出每按一次start按钮,addBall方法都会启动一个新线程,所以每一个小球都是在本身的线程中绘制的。eclipse

1.3 选作:程序改写:程序运行时,每一个小球都是从固定位置出发。如何改写该程序,使得当点击start时,每一个小球能够从不一样位置出发、以不一样的步进移动?

答:可使用Math.random()方法将ball.java中的xydxdy的值设置成随机数来达到每次点击start时使小球以不一样位置出发和以不一样步前进。

函数

1.4 选作:不一样小球的移动轨迹如出一辙。改造程序,使得每一个小球运行轨迹不彻底同样,好比有的能够走余弦轨迹、有的能够走方波轨迹、有的走布朗运动、有的走五角星,等等。

2. 实验总结:题集(多线程)

2.1 题目:Thread、PrintTask、Runnable与匿名内部类。

并回答:a)经过定义Runnable接口的实现类来实现多线程程序比经过继承自Thread类实现多线程程序有何好处?b) 6-1,6-3,6-11实验总结。
答:(a).使用实现Runnable接口来实现多继承的程序的好处:性能

  • 1.Java中不支持多继承,一个类能够继承多个接口,可是只能继承一个父类,所以使用实现接口的方法能够避免继承的局限。
  • 2.使用Runnable接口适合于资源的共享,以下两张图片:


能够看出,虽然两个程序中都建立了两个线程,可是使用Runnable实现多线程使两个线程一块儿完成两个任务,达到资源共享的目的。
(b).Thread实验总结
这题中要注意MyThread类有一个有参构造函数,用于接收循环次数,而后要将标识信息放在for循环外,其余就是普通的打印输出了。
Runnable实验总结
这题使用了匿名内部类来实现Runnable接口的run方法来完成程序。获取当前的线程名字要用Thread.currentThread().getName(),而后就是按要求输出就OK了。
PrintTask实验总结
这题……除了类名和最后输出的标识信息,其余都和6-1同样hhhh学习

2.2 使用Lambda表达式改写6-3

Thread t1 = new Thread(() -> {
            System.out.println(mainThreadName);
            System.out.println(Thread.currentThread().getName());
            System.out.println(Arrays.toString(Thread.class.getInterfaces()));
        });

2.3 题目:6-2(Runnable与中止线程)。回答:须要怎样才能正确地中止一个运行中的线程?

答:在Java的Thread类中提供了一个stop()方法用来终止线程,不过,由于这个方法会将执行到一半的线程强行终止,,不能保证线程的资源正确释放,因此已经被废弃了。
如今在咱们须要中止一个线程的时候通常使用一个boolean类型的变量来终止线程,这样可使用while语句,在运行中经过改变boolean标记值来使while循环退出来达到中止线程的做用。
咱们还可使用interrupt方法来终止线程,可是,这种方法并不适用于一个正在运行的线程,它是用于中止一个受到阻塞的线程,经过使受阻塞的线程抛出异常来退出阻塞的状态。
所以仍是使用boolean类型的变量来终止线程的方法比较好。测试

2.4 选作:6-8(CountDownLatch)实验总结

这题就是跟着注释一步步写下来就能够了。
CountDownLatch类是一个同步计数器,构造时传入int参数,用于表示任务的个数。这个参数是计数器的初始值。
使用Executors类的newFixedThreadPool方法能够建立一个固定线程数的线程池。线程

2.5 选作:6-9(集合同步问题)实验总结

要想让ArrayList等达到线程安全,只须要用synchronizedList修饰list便可。synchronizedList经过对部分操做加上synchronized来保证线程安全。

2.6 选作:较难:6-10(Callable),并回答为何有Runnable了还须要Callable?实验总结。

3. 互斥访问

3.1 修改TestUnSynchronizedThread.java源代码使其能够同步访问。(关键代码截图,需出现学号)

其实只要在Counter类的对id进行操做的两个方法前加上synchronized关键字就能够了,它能够经过给共享资源上一道锁来使在同一个时间内只容许一个线程访问共享资源,以此来实现同步机制。

3.2 选作:进一步使用执行器改进相应代码(关键代码截图,需出现学号)


4. 互斥访问与同步访问

完成题集6-4(互斥访问)与6-5(同步访问)

4.1 除了使用synchronized修饰方法实现互斥同步访问,还有什么办法可使用synchronized实现互斥同步访问,使用代码说明(请出现相关代码及学号)?

答:还能够经过synchronized的同步代码块来实现互斥访问。
修改后的方法以下:

4.2 同步代码块与同步方法有何区别?

答:同步方法直接在方法名前面加synchonrized关键字来实现对对象的加锁,而同步代码块则是在方法的内部使用synchonrized关键字来加锁,所以,同步方法的做用范围比同步代码块大,其性能则不如同步代码块。

4.3 实现互斥访问的原理是什么?请使用对象锁概念并结合相应的代码块进行说明。当程序执行synchronized同步代码块或者同步方法时,线程的状态是怎么变化的?

答:实现互斥访的原理是程序经过给共享资源上一道锁,让给定时间内只容许一个线程来访问共享资源。
下面经过分析一个从书上看到的模拟火车站售票系统的程序来讲明同步机制运行时的线程状态,代码以下

public class ThreadSafeTest implements Runnable {
    int num = 10;       //设置当前总票数
    
    public void run() {
        while (true) {
            synchronized ("") {
                if (num > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println("tickets" + --num);
                }
            }
        }
    }
    
    public static void main(String[] args) {
        ThreadSafeTest t = new ThreadSafeTest();    //实例化类对象
        Thread tA = new Thread(t);  // 以该类对象分别实例化4个线程
        Thread tB = new Thread(t);
        Thread tC = new Thread(t);
        Thread tD = new Thread(t);
        tA.start(); // 分别启动进程
        tB.start();
        tC.start();
        tD.start();
    }
}

当咱们运行程序的时候,一个线程先开始工做,给num上了一把锁,而后其余三个线程就不能再访问这个区域,等到tA执行完售票的操做将票数减1并输出后,对象锁被释放,而后下一个线程又开始工做,再给num上了一把锁,以此类推,直到票数为0后结束进程。

4.4 Java多线程中使用什么关键字实现线程之间的通讯,进而实现线程的协同工做?

  • synchronized 关键字
  • wait/notify方法
  • volatile关键字

5. 线程间的合做:生产者消费者问题

5.1 运行MyProducerConsumerTest.java。正常运行结果应该是仓库还剩0个货物。多运行几回,观察结果,并回答:结果正常吗?哪里不正常?为何?

能够看出运行结果不正常,最后还有出现货物有剩余的状况,这是由于Producer和customer的存取速度不同,致使二者不能很好的进行交互,从而使最后剩于货物结果出现异常。

5.2 使用synchronized, wait, notify解决该问题(关键代码截图,需出现学号)




运行结果:

5.3 选作:使用Lock与Condition对象解决该问题。

运行结果:

6. 面向对象设计做业-图书馆管理系统

6.1 系统的功能模块表格,表格中体现出每一个模块的负责人。

学生 负责任务
陈晓菲 图书管理模块
廖文姑 菜单类及测试类
贾海涛 用户管理模块

6.2 运行视频

6.3 讲解本身负责的模块,并粘贴本身负责模块的关键代码(出现学号及姓名)。

我负责的是图书管理模块,里面包含两个类,图书馆类和图书类,用于进行书籍的管理。
图书类:

//201621123031
public class Book {
    private String name;
    private long ID;
    private String category;
    
    public Book(String name,long ID,String category){
…………
    }
    /*
*get()和set()和方法
*/

图书馆类:

public class Library {
    public enum LendOrBack{ //用于变动借书还书后的书籍变化
        LEND,BACK
    }
    private static Map<Book,Integer> books=new TreeMap<Book,Integer>(); //用于存储图书信息及数量
    static{
        initializeBookStore();  
    }
    private static void initializeBookStore(){
        Book book1 = new Book("红与黑",111,"文学类");
        Book book2 = new Book("平凡的世界",112,"文学类");
        Book book3 = new Book("双城记",113,"文学类");
        Book book4 = new Book("资本论",114,"经济类");
        Book book5 = new Book("孙子兵法",115,"军事类");
        Book book6 = new Book("人间词话",116,"艺术类");
        Book book7 = new Book("诗经",117,"艺术类");
        Book book8 = new Book("楚辞",118,"艺术类");
        Book book9 = new Book("战国策",119,"历史类");
        Book book10 = new Book("国家地理",120,"地理类");
        books.put(book1, 30);
        books.put(book2, 50);
        books.put(book3, 40);
        books.put(book4, 10);
        books.put(book5, 20);
        books.put(book6, 15);
        books.put(book7, 35);
        books.put(book8, 40);
        books.put(book9, 5);
        books.put(book10, 10);
        
    }

    public static Book peekBook(long ID) {  //根据ID获取某本书籍的信息
        Iterator<Map.Entry<Book, Integer>> m = books.entrySet().iterator();
        while(m.hasNext()){
            Map.Entry<Book, Integer> e = m.next();
            if(e.getKey().getID()==ID&&e.getValue()>0)
                return e.getKey();
        }
        return null;
    }
    public static ArrayList<Book> peekBooks(String name) {  //根据书名获取某本书籍的信息
………………              
    }
    
    public static void add(Book book,int num){  //添加书籍
        if(books.get(book)==null){
            books.put(book, num);
        }
        else{
            books.put(book, books.get(book)+num);
        }
        
    }
    public static void remove(Book book){   //移除书籍
        books.remove(book);
    }
    
    public static void lendOrBack(Book book,LendOrBack action){ //进行借书还书操做时书籍数量的变动
        switch(action) {
        
        case LEND:{
            books.put(book, books.get(book)-1);
            break;
        }
        case BACK:{
            books.put(book, books.get(book)+1);
            break;
        }
        
        
        }
    }

    
    public static void display(){   //展现图书馆的藏书
        Iterator<Map.Entry<Book, Integer>> m = books.entrySet().iterator();
        
        System.out.println("     书名   \t\t\tID\t\t类别\t\t数量");
        while(m.hasNext()){
            Map.Entry<Book, Integer> e = m.next();
            if(e.getValue()>0)
                System.out.println(e.getKey().toString()+"\t\t"+e.getValue());
        }
        System.out.println();
        
    }

}

7. 选作:使用其余方法解决题目5的生产者消费者问题。

7.1 使用BlockingQueue解决生产者消费者问题关键代码截图


7.2 说明为何不须要显示的使用wait、notify就能够解决同步问题。这样解决相比较wait、notify有什么优势吗?

答:BlockingQueue是阻塞队列,当队列中没有数据的状况下,消费者线程会被自动阻塞,直到有数据进入队列,而当队列中填满数据的时候,生产者的全部线程都会被自动阻塞,直到队列中有空的位置,线程才会被自动唤醒。所以它不须要使用wait、notify就能够达到解决同步问题的方法,这样的方法比wait、notify更简单且更方便。

3.码云及PTA

题目集:多线程

3.1. 码云代码提交记录

  • 在码云的项目中,依次选择“统计-Commits历史-设置时间段”, 而后搜索并截图
  • 必须出现几个要素:提交日期-用户名(姓名与学号)-不提交说明

3.2 截图"多线程"PTA提交列表

须要有两张图(1. 排名图。2.PTA提交列表图)


3.3 统计本周完成的代码量

须要将每周的代码统计状况融合到一张表中。

周次 总代码量 新增代码量 总文件数 新增文件数
2 607 607 15 15
3 1642 1035 33 18
5 2044 402 42 9
6 2874 830 57 15
7 3161 287 63 6
8 4299 1138 72 9
9 4831 532 81 9
10 5475 644 93 12
11 5958 483 102 9
12 6819 861 116 14

5. 选作:学会使用Eclipse进行调试

观看相关调试视频

5.1 简述使用Eclipse进行调试须要几步?调试时F5, F6, F7快键键各有什么不一样?什么状况该使用哪一个快捷键?

使用eclipse调试的步骤:

  • 在须要调试的程序的左边栏上双击来设置断点
  • 点击调试按钮进入调试透视图
  • 以debug方式运行程序进行调试

F5:单步执行程序,遇到方法时进入。在碰到方法内部出现问题的时候,就要用F5去深刻方法内部来进行调试。
F6:单步执行程序,遇到方法时跳过。在不用理会深层操做的时候使用F6。
F7:单步执行程序,从当前方法跳出。当进入某个方法内部想跳出来时使用F7。

5.2 实验任务书中的题目5:使用Eclipse进行调试中的5.1,如何使用Eclipse的调试功能发现当读取“蓝山兰”这行不会出错的缘由?

5.3 任务书5.2,截图证实你会使用条件断点。

5.4 选作:调试MessageBoard.zip中的系统直至能够正常运行。说明你是怎么找到该系统中的错误?使用到了什么调试技巧?

相关文章
相关标签/搜索