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

一、前言


 

 面向对象新的三周是属于电梯的,电梯是我们日常生活中随处可见的,所以这个作业内容特别好理解,而且看起来不会太难,但是真的写起来却会发现各种各样的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他人的程序都是极好的。

 

posted @ 2019-04-24 15:43  藤原千花  阅读(160)  评论(3编辑  收藏  举报