20145217 《Java程序设计》第六周学习总结
教材学习内容总结
本章主要讲输入与输出。
10.1
- 若要将数据从来源中取出,可以使用输入串流;若要将数据写入目的地,可以使用输出串流。在
java中,输入串流代表对象为java.in.InputStream的实例;输出串流代表对象为java.io.Outputstream的实例。 - 在来源与目的地都不知道的情况下可以设计一个通用的
dump()方法,该方法接受InputStream与OutputStream实例,分别代表读取数据的来源、输出的目的地。 - 每次从
Inputstream读入的数据,都会先置入byte数据,她的read()方法会尝试读入btye的数据,并返回读入的字节。 - 要将某个文档读入并另存为另一个数据,可以由命令行操作如下
java cc.openhome.Copy c:workspaceMain.java C:workspaceMain.txt
10.2
FileInputStream是InputStream的子类,主要操作InputStream的read()抽象方法;FIleOutputStream是OutputStream的子类,主要操作其write()的操作方法- 可以使用
System的setIn()方法指定InputStream实例,重新指定标准输入来源 ByteArrayInputStream是InputStream的子类,可以指定byte数据创建实例,主要操作其read()抽象方法;ByteArrayOutputStream是OutputStream的子类,主要操作其write()的操作方法
10.3
- 串流装饰器本身并没有改变
InputStream和OutputStream的行为,只是在得到数据之后,再做一些加工处理。 BufferedInputStream与BufferedOutputStream主要在内部提供缓冲区功能。DataInputStream与DataOutputStream主要提供读取、写入java基本数据类型的方法,会自动在指定的类型与字节之间转换。
10.4
Reader.Writer也有一些装饰器类可供使用,如果串流处理的字节数据,实际上代表某些字符的编码数据,而你想要将这些字节数据转换为对应的编码字符,可以使用InputStreamReader和OutputStreamWriter。- 若要使用
CharUtil.dump()读入文档、转为字符串并显示在文本模式中。
10.5
- 如果处理串流字节数据,将这些字节数据转换为对应的编码制度,可以使用
InputStringReader、InputStringWriter打包。 BufferedReader、BufferedWriter可对Reader、Writer提供缓冲区,printWriter与PrintStream处理可以对OutputStream打包之外,Printwriter还可以对writer进行打包,提供print()、println()、format()等方法。
11.1线程
- 单线程程序:启动的程序从
main()程序进入点开始至结束只有一个流程 - 多线程程序:程序有多个流程
- 课本代码运行截图:

11.2Thread、Daemon
-
Thread:如果想要加装主线程,就要创建Thread实例,要启动额外的主线程就是调用Thread实例的start()方法 -
额外线程执行流程的进入点,有两种方式:可以定义在
Runnable的run()方法中继承Thread类,重新定义run()方法,主线程会从main()方法开始执行,直到main()方法结束后停止JVM。如果主线程中启动了额外线程,默认会等待被启动的所有线程都执行完run()方法才中止JVM;如果一个Thread被标示为Daemon线程,在所有的非Daemon线程都结束时,JVM自动就会终止。从main()方法开始的就是一个非Daemin线程,可以使用setDaemon()方法来设定一个线程是否为Daemon线程。使用isDaemon()方法可以判断线程是否为Daemon线程。 -
在调用
Thread实例start()方法后,基本状态为可执行(Runnable)、被阻断(Blocked)、执行中(Running)。线程看起来像是同时执行,但事实上同一时间点上,一个CPU只能执行一个线程,只是CPU会不断切换线程,且切换动作很快,所以看起来像是同时执行。 -
setPriority():线程有其优先权,可使用Thread的setPriority()方法设定优先权,可设定值为1到10,默认是5,超出1到10外的设定值会抛出IllegalArgumentException。 -
改进效能的方式:运用多线程,当某线程进入
Blocked时,让另一线程排入CPU执行,避免CPU空闲下来。 -
interrupt():一个进入Blocked状态的线程,可以由另一个线程调用,该线程的interrupt()方法,让它离开Blocked状态。
11.3ThreadGroup
- 每个线程都属于某个线程群组。每个线程产生时,都会归入某个线程群组,这视线程在那个群组中产生,如果没有指定,则归入产生该子线程的线程群组,也可以自行指定线程群组,线程一旦归入某个群组,就无法再更换。
setMaxpriority():设定群组中所有线程最大优先权。activeCount():取得群组的线程数量,enumerate()方法要传入Thread数组,这会将线程对象设定至每个数组索引。uncaughtException():群组中某个线程发生异常而未捕捉时,JVM 会调用此方法进行处理。如果ThreadGroup有父ThreadGroup,就会调用父ThreadGroup的uncaughtException()方法,否则看看异常是否为ThreadDeath实例,若是则什么都不做,若不是则调用异常的printStrackTrace(),如果必须定义ThreadGroup中的线程异常处理行为,可重新定义此方法。uncaughtException()方法第一个参数可取得发生异常的线程实例,第二个参数可取得异常对象。
11.4synchronized
- 每个对象都会有个内部锁定,或称为监控锁定。被标示为
synchronized的区块将会被监控,任何线程要执行synchronized区块都必须先取得指定的对象锁定。如果在方法上标示synchronized,则执行方法必须取得该实例的锁定。线程若因尝试执行synchronized区块而进入Blocked,在取得锁定之后,会先回到Runnable状态,等待CPU排版器排入Running状态。java的synchronized提供的是可重入同步,也就是线程取得某对象锁定后,若执行过程中又要执行synchronized,尝试取得锁定的对象来源又是同一个,则可以直接执行。
11.5volatile
synchronized要求达到的所标示区块的互斥性与可见性,互斥性是指synchronized区块同时间只能有一个线程,可见性是指线程离开synchronized区块后,另一线程接触到的就是上一线程改变后的对象状态。- 可以在变量上声明
volatile,表示变量是不稳定的、易变的,也就是可能在多线程下存取,这保证变量的可见性,也就是若有线程变动了变量值,另一线程一定可以看到变更。被标示为volatile的变量,不允许线程快取,变量值的存取一定是在共享内存中进行。 volatile保证的是单一变数的可见性,线程对变量的存取一定是在共享内存中,不会在自己的内存空间中快取变量,线程对共享内存中变量的存取,另一线程一定看得到。
11.6等待与通知
wait()、notify()、notifyAll()是Object定义的方法,可以通过这三个方法控制线程释放对象的锁定,或者通知线程参与锁定竞争。wait():执行synchronized范围的程序代码期间,若要调用锁定对象的wait()方法,线程会释放对象锁定,并进入对象等待集合而处于阻断状态,其他线程可以竞争对象锁定,取得锁定的线程可以执行synchronized范围的程序代码。wait()可以指定等待时间,时间到之后线程会再次加入排班,如果指定时间为0或不指定,则线程会持续等待,只到被中断或是告知可以参与排班。noyify():被竞争锁定的对象调用noyify()时,会从对象等待集合中随机通知一个线程加入排班,再次执行synchronized前,被通知的线程会与其他线程共同竞争对象锁定。notifyAll():如果调用notifyAll(),所有等待集合中的线程都会被通知参与排班,这些线程会与其他线程共同竞争对象锁定。
11.7Lock、ReadWriteLock 与 Condition
Lock接口主要操作类之一为ReentrantLock,可以达到synchronized的作用,也提供额外的功能。想要锁定Lock对象,可以调用其lock方法,只有取得Lock对象锁定的线程,才可以继续往后执行程序代码,要接触锁定,可以调用Lock对象的unlock()。Lock接口还定义了tryLock()方法,如果线程调用tryLock()可以取得锁定会返回true,若无法取得锁定并不会发生阻断,而是返回false。ReadWriteLock接口定义了读取锁定与写入锁定行为,可以使用readLock()、writeLock()方法返回Lock操作对象。ReentrantReadWriteLock.ReadLock操作了Lock接口,调用其lock()方法时,若没有任何ReentrantReadWriteLock.WriteLock调用过lock()方法,也就是没有任何写入锁定时,就可以取得读取锁定。ReentrantReadWriteLock.WriteLock操作了Lock接口,调用其lock()方法时,若没有任何ReentrantReadWriteLock.ReadLock或ReentrantReadWriteLock.WriteLock调用过lock()方法,也就是没有任何读取或写入锁定时,才可以取得写入锁定。validate():验证戳记是不是被其他排他性锁定取得了,如果是的话返回false,如果戳记是 0 也会返回false .Condition。Condition接口用来搭配Lock,最基本的用法就是达到Object的wait()、notify()、notifyAll()方法的作用。signal():要通知等待集合中的一个线程,则可以调用signal()方法。signalAll():如果要通知所有等待集合中的线程,可以调用 signalAll()。- 一个
Condition对象可代表有一个等待集合,可以重复调用Lock的newCondition(),取得多个Condition实例,这代表了可以有多个等待集合。
代码调试中的问题和解决过程
书上的代码运行不了,但都能编译通过,这对我们初学者造成不少麻烦,虽然认认真真学完但依然不太会用,目前依然未解决。
上传代码到git:

学习进度条
| 代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
|---|---|---|---|---|
| 目标 | 5000行 | 30篇 | 400小时 | |
| 第三周 | 300/600 | 2/6 | 20/50 | |
| 第四周 | 300/900 | 2/8 | 16/66 | |
| 第五周 | 300/1200 | 2/10 | 16/82 | |
| 第六周 | 300/1500 | 2/12 | 16/98 |