20145208 《Java程序设计》第6周学习总结
20145208 《Java程序设计》第6周学习总结
教材学习内容总结
输入与输出
InputStream与OutputStream
- 从应用程序角度来看,如果要将数据从来源取出,可以使用输入串流;如果要将数据写入目的地,可以使用输出串流。在Java中,输入串流代表对象为java.io.InputStream实例,输出串流代表对象为java.io.OutputStream实例。无论数据源或目的地为何,只要设法取得InputStream或OutputStream的实例,接下来操作输入/输出的方式都是一致的,无须理会来源或目的地的真正形式。
- java.io包中包含了流式I/O所需要的所有类。在java.io包中有四个基本类:InputStream、OutputStream及Reader、Writer类,它们分别处理字节流和字符流:
类 | 说明 |
---|---|
File | 文件类 |
RandomAccessFile | 随机存取文件类 |
InputStream | 字节输入流 |
OutputStream | 字节输出流 |
Reader | 字符输入流 |
Writer | 字符输出流 |
标准输入输出
- System.in: 标准输入,默认关联到键盘(终端输入)
- System.out: 标准输出,默认关联到显示器(终端输出)
- System.err: 标准错误,默认关联到显示器(终端输出)
- 输入输出重定向:setIn,setOut,setErr
线程与并行API
线程
- 在java中,如果想在main()以外独立设计流程,可以撰写类操作java.lang.Runnable接口,流程的进入点是操作在run()方法中。如果想要为JVM加装CPU,就是创建Thread实例,要启动额外CPU就是调用Thread实例的start()方法,额外CPU执行流程的进入点,可以定义在Runnale接口的run()方法中。
- 操作Runnable接口的好处就是较有弹性,你的类还有机会继承其他类。若继承了Thread,那该类就是一种Thread,通常是为了直接利用Thread中定义的一些方法,才会继承Thread来操作。
- 1、Runnable适合于多个相同程序代码线程去处理统一资源的情况,把虚拟的cpu(线程)同程序的代码,数据有效分离,较好体现面向对象的编程的思想
- 2、Runnable可以避免由于java的单继承机制带来的局限。可以再继承其他类的同时,还能实现多线程的功能。
- 3、Runnable能增加程序的健壮性。代码能够被多个线程共享。
线程生命周期
- 生命周期的五种状态
- 新建(new Thread):当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。例如:Thread t1=new Thread();
- 就绪(runnable):线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();
- 运行(running):线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
- 死亡(dead):当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。自然终止:正常运行run()方法后终止。异常终止:调用stop()方法让一个线程终止运行。
- 堵塞(blocked):由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。正在等待:调用wait()方法。(调用motify()方法回到就绪状态)。被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)
Thread基本状态图
- 在调用Thread实例start()方法后,基本状态为可执行(Runnable)、被阻断(Blocked)、执行中(Running)。实例化Thread并执行start()方法后,线程进入Runnable状态,此时线程尚未真正开始执行run()方法,必须等待排班器排入CPU执行,线程才会执行run()方法,进入Running状态,线程看起来像是同时执行,但事实上,同一个时间点,一个CPU还是只能执行一个线程,只是CPU会不断切换线程,且切换动作很快,所以看起来像是同时执行。线程有优先权,可用Thread的setPriority()方法设定优先权。最小值为1,最大值为10,默认是5。数字越大,优先权越高,排班器越优先排入CPU,如果优先权相同,则输流执行。运用多线程,当某线程进入Blocked时,让另一线程排入CPU执行,避免CPU闲下来,经常是改进效能的方式之一。
并行API
- 基于Thread类和Runnable接口编程很容易实现基本的并行编程任务,但实现复杂的并行程序就会比较困难,因为要详细考虑资源的同步访问以及设计必要数据结构支持同步访问。从Java5后,Java平台提供了java.util.concurrent包以及HighLevelAPI简化并行编程模型,并提供了很多支持同步访问数据结构满足编程需要。
- Lock(锁对象):相对与Thread模型的隐式的锁对象,Lock提供了显式的锁操作从而简化应用程序。Executors:提供了一组HighLevelAPI用来执行和管理并行任务。 ConcurrentCollections(并行集合):包含了一组支持并行处理的数据结构,大大简化了并行编程难度。AtomicVariables(原子变量):减少了同步操作并且避免数据不一致。Fork/Join框架:提供了进程操作的支持。
- CopyOnWriteArrayList操作了List接口,顾名思义,这个类的实例在写入操作时,内部会建立新数组,并复制原有数组索引的参考,然后在新数组上进行写入操作,写入完成后,再将内部原参考旧数组的变量参考至新数组。对于一个很少进行写入操作,而使用迭代器频繁的情景下,可以使用CopyOnWriteArrayList提高迭代器操作的效率。BlockingQueue是Queue的子接口,新定义了put()与take()等方法,线程若调用put()方法,在队列已满的情况下会被阻断,线程若调用了take()方法,在队列为空的情况下会被阻断。ConcurrentMap是Map的子接口,其定义了putIfAbsent()、remove()、replace()等方法。这些方法都是原子操作。putIfAbsent()在键对象不存在ConcurrentMap中时,才可置入键/值对象,否则返回键对应的值对象。remove()只有在键对象存在,且对应的值对象等于指定的值对象,才将键/值对象移除。replace()有两个版本,其中一个版本是只有在键对象存在,且对应的值对象等于指定的值对象,才将值对象置换,另外一个版本是在键对象存在时,将值对象置换。
教材学习中的问题和解决过程
问题一
- p325 TortoiseHareRace.java: 单线程版龟兔赛跑
- 代码中的flags数组存储了随机生成的
true
和fales
,用来判断兔子是否睡觉。如果定义flags数组的时候只存储了true
那么兔子就会一直睡觉zzzz。如下图:
- 代码中的flags数组存储了随机生成的
- 因为数组是通过random随机生成的,所以每次的运行结果并不唯一。
问题二
-
p326 Tortoise.java Hare.java TortoiseHareRace2.java: 多线程版的龟兔赛跑
- 代码运行出来的结果有问题如图:
- 代码运行出来的结果有问题如图:
-
这样的结果不是一个理想的结果,因为我想要的二者交替显示的,而且想要一方到十步程序就可以终止的。
-
解决方案:
- 还想思考具体的方案。
问题三
- p306 Copy.java: IO.java的测试类
- 我要自己建一个id.txt的文件,里面输入自己的学号,再把id.txt拷贝到id1.txt中。在IDEA中实现的代码如下:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class StreamCopy {
public static void main(String[] args) throws IOException {
StreamIO.dump(
new FileInputStream("C:/Users/Cai Ye/IdeaProjects/HelloWorld/src/CH6/id.txt"),
new FileOutputStream("C:/Users/Cai Ye/IdeaProjects/HelloWorld/src/CH6/id1.txt")
);
}
}
问题四
- p330 DaemonDemo.java: Daemon线程
- 注释掉thread.setDaemon(true);之后,运行结果如下:
- 系统不断地输出Orz,因为setDaemon()被注释掉了,没有设定为true。
问题五
- p334 JionDemo.java: join
- 对比测试join的功能
- 下图是有join的时候
- 下图是没有join的时候
- 可以看出来是join将输出插入进去的
代码调试中的问题和解决过程
问题一
- p339 ArrayListDemo.java
- 该代码线程不安全,如图:
解决方案:
- 我根据书中给的三种方法,逐个进行了测试,但是线程依旧不是安全的,运行结果稍有不同,原先的代码是运行之后马上抛出了异常,修改之后是停顿了几秒才抛出异常,但是至今还是没有解决这个问题。
问题二
- p343 DeadLockDemo.java: 死锁
- 运行的时候可能会出现死锁
- 正常运行如下:
- 锁死时如图:无输出
- 解决方案:
- 还在尝试使用tryLock的方法解决。
其他(感悟、思考)
- 本周的学习中,我遇到了很多的难处,本周所学习的两章内容比之前学习的内容难很多,尤其是许多代码虽然可以运行通过,但是我知道代码是有问题的,是不理想的,在本周里,我解决的一些我遇到的问题,但是还有一部分问题我没有完成修正,我觉得我需要深入学习API,提升我对代码的修改能力。
- 在学习JAVA的这段时间,绝对是我大学以来学习时间最多的一段时间,因为JAVA的学习真的需要我付出很多很多时间,每周两章的学习,对自己是一种考验和鞭策,想学好的话,那么必然会耗费大量的时间,但是收获也会很多,我相信,认真学习的知识会记得更久很深刻。
- 在一周一周的学习中,我越来越发现无论是JAVA还是C语言,对他们的算法的学习也是在锻炼自己的思维思路,光是学习怎么敲代码是不可以的,关键是要有一条清晰又正确的思路,编程思路决定了我们的编写代码的速度和质量,还有我们修改代码的效率和成功率。
- 虽然本周的学习中我遇到了很多的挫折,但是我没有决定气馁,我愈发的可以体会到JAVA学习中思考问题和解决问题的愉悦感。希望我在未来的JAVA学习中不断地进步,学有所得。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | 成为超过高级初学者的存在 |
第一周 | 150/150 | 1/3 | 15/15 | 第一个JAVA程序 |
第二周 | 150/300 | 2/5 | 20/35 | 第一次对教材代码进行自己的修改 |
第三周 | 400/700 | 2/7 | 20/50 | 熟练的进行代码托管 |
第四周 | 1210/1910 | 2/9 | 30/80 | 在敲代码的时候有自己思考 |
第五周 | 1083/2993 | 1/10 | 40/120 | 学习API |
第六周 | 1061/4054 | 2/12 | 50/170 | 了解到了输入输出与线程 |
参考资料
posted on 2016-04-10 16:57 20145208蔡野 阅读(242) 评论(2) 编辑 收藏 举报