• 初识多线程——第二单元学习小结


    一、前言


     面向对象新的三周是属于电梯的,电梯是我们日常生活中随处可见的,所以这个作业内容特别好理解,而且看起来不会太难,但是真的写起来却会发现各种各样的BUG。

    这单元作业,总的来说是学习多线程,特别是线程间的同步与互斥。我的三次作业就是建立在“生产者-消费者”模型之上的:

    一个输入线程——生产者,一个或多个电梯线程——消费者。每次作业就只是增加一些需求,总的架构是没有改变的。

    二、困惑了很久的线程BUG


      这个问题是我在写第一次电梯作业时候发现的,这份代码把代码1放前面有正常输出,而代码2放前面则没有输出,即使ArrayList线程不安全也应该不会出现这种情况。

     1 import java.util.ArrayList;
     2 
     3 public class Main {
     4     static ArrayList<Integer> ArrayInteger = new ArrayList<>();
     5 
     6     public static void main(String[] args) {
     7         Elevator elevator = new Elevator();
     8         Thread thread = new Thread(elevator);
     9         thread.start();
    10         while (true) {
    11             try {
    12                 ArrayInteger.add(null); //代码1
    13                 Thread.sleep(1000);   //代码2
    14             } catch (Exception e) {
    15             }
    16         }
    17     }
    18 }
    19 
    20 class Elevator implements Runnable {
    21     @Override
    22     public void run() {
    23         int i = 0;
    24         while (true) {
    25             if (i < Main.ArrayInteger.size()) {
    26                 System.out.println("BREAK");
    27                 break;
    28             }
    29         }
    30     }
    31 }

    后来在讨论区提出问题以后才知道原因:

      JIT或HotSpot编译器在server模式和client模式编译不同,server模式为了使线程运行更快,如果其中一个线程更改了变量的值,那么另外一个线程会看不到,因为另外一个线程为了使得运行更快所以从寄存器或者本地cache中取值,而不是从内存中取值,那么使用volatile后,就告诉不论是什么线程,被volatile修饰的变量都要从内存中取值。(内存栅栏)

    三、代码分析


    1.第一次作业

      第一次作业代码特别简单,我建立了两个线程,Input和Elevator,采用轮询的方式获取输入的请求,没用notify和wait,然后Elevator线程直接按照每一条请求进行处理,没有考虑捎带的情况。

      但是值得一提的是,储存请求的类要用线程安全的类,Collections.synchronizedList,就是一个不错的选择。

    圈复杂度:

     UML类图:

       可以看出这次结构还是很简单的,但是两个线程的圈复杂度较高,是因为我在里边写了while(true)还有if-else导致的。

    2.第二次作业

      第二次作业的难度要比第一次作业高了一点,但这时我们已经学习了一些线程安全的知识,把第一次作业的架构改改即可完成。

    圈复杂度:

    UML类图:

       这次作业我把电梯线程改成了具有捎带功能的电梯,它采用选择第一位进入电梯的人作为主请求,每到一层楼都会检索当前楼层有没有可以进的人,有则捎带。这个对线程安全的要求就高了一些,但是在关键的地方加几个synchronized即可解决。

    3.第三次作业

      这次作业狠狠地打击了我的自信心,因为我强测获得了0分,把B电梯的科可停靠楼层写错了。这次作业的架构和第二次的类似, 把第二次的电梯线程开了三个,然后写了一个调度器线程。

    圈复杂度:

    UML类图:

       这次作业的难点在于电梯如何换乘,因为三部电梯的可到达区间都不相同,因此要判断电梯的换乘问题(正常的电梯不应该是乘客自己考虑吗)。然后我就设计了一个调度器的线程,计算顾客的请求是否需要换乘,然后再将请求输入到电梯线程。这里需要注意,必须等到达之后才能将换乘请求输入到换乘的电梯。

    四、心得体会



      这几次作业让我初识了并发式编程这一项编程的基本功,他和我们以前学的编程都不相同。虽然单线程和多线程都可以运行在同一个CPU核上,但是多线程却特别容易出线程不安全的BUG,而单线程却一般只会错在算法上。总的来说这三次作业有一定的收获,但我自身有很大的不足之处,我本可以多花一些时间优化算法,写评测机。但我总是懒得写,或者害怕出BUG。还有写完程序后要编写完备的测试样例,无论是测试自己的程序还是hack他人的程序都是极好的。

  • 相关阅读:
    [iOS UI进阶
    [iOS UI进阶
    [iOS基础控件
    [iOS基础控件
    [iOS基础控件
    [iOS基础控件
    为什么会使用内部临时表
    Django日志模块配置
    mysql join语句分析(一)
    mysql误删数据以及kill语句工作原理
  • 原文地址:https://www.cnblogs.com/cherishlove/p/10762752.html
Copyright © 2020-2023  润新知