输入与输出
20145217 《Java程序设计》第六周学习总结
教材学习内容总结
本章主要讲输入与输出。
10.1
- 若要将数据从来源中取出,可以使用输入串流;若要将数据写入目的地,可以使用输出串流。在
java
中,输入串流代表对象为java.in.InputStream
的实例;输出串流代表对象为java.io.Outputstream
的实例。 - 在来源与目的地都不知道的情况下可以设计一个通用的
dump()
方法,该方法接受InputStream
与OutputStream
实例,分别代表读取数据的来源、输出的目的地。 - 每次从
Inputstream
读入的数据,都会先置入byte
数据,她的read()
方法会尝试读入btye
的数据,并返回读入的字节。 - 要将某个文档读入并另存为另一个数据,可以由命令行操作如下
java cc.openhome.Copy c:\workspace\Main.java C:\workspace\Main.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 |