07-03-01 线程_IO串讲
进程是一套顺序执行的指令。
同时开辟并行执行序列即多线程 run() 结束,线程就结束
JVM就是一个进程,在JVM中分出线程
进程数据空间独立;线程数据空间共享, 线程间通信更容易
分配的方式不如线程好,能充分使用CPU的资源。 共享数据就要加锁、解锁,会降低效率。
OS有 时分共享操作系统 和 实时操作系统
时分共享操作系统 时间片 调度系统把每个时间片分给多个进程 性能调优是根据时间片划分, 时间片会影响整个操作系统。
为了让某些工作并行,在进程主线程中开辟多个线程,这就是线程的机制。
我们关心的是被线程共享的数据,主要看synchronized加在何处。往往不加在线程里面,而是在共享的对象上。
只有运行状态的线程才有机会执行代码!
只有等到所有线程终止,进程才结束!
当一个对象分配给某线程锁标记时,其它线程不能访问同步方法,但能访问非同步方法!
起线程的两种方式:1、继承Thread,覆盖run()方法,用start()启动
2、实现Runnable接口,用来构造线程对象。(线程对象不是线程,但代表一个线程)
例子 ProducerConsumer.java
每一个对象都有一个互斥锁标记(monitor),这个锁标记是准备分配给线程的,且只能分配给一个线程
用法一:
synchronized(o){
原子操作代码块
}
哪个线程能拿到o的锁标记,哪个线程才能进入这个同步代码块,用完后释放锁标记
未加同步可能造成数据不一致和数据不完整的缺陷。
用法二:
public synchronized void method(){ …}
等价于:
Public void method(){
Synchronized(this){...}
}
在整个方法中,对this加锁。
l 构造方法 不能同步,因为还没有当前对象
l 抽象方法 也不能同步,因为子类覆盖方法后可以是非同步方法,父类就有多余的话。
每一个对象都有一个锁池,装的是等待该对象锁标记的线程。
一个线程可以同时拥有多个对象的锁标记
synchronized(o1){ //同步代码块是可以嵌套的
synchronized(o2){
}
}
当一个线程阻塞在A对象的锁池里的时候,不会释放其所拥有的其他对象的锁标记
每个线程不释放自己拥有的资源,却申请别的线程的资源,就可能造成死锁问题
线程t1: o.wait()
前提: 必须在对o加锁的同步代码块里
1. t1会释放其所拥有的所有锁标记
2. t1会进入o的等待队列
线程t2: o.notify() / o.notifyAll()
前提:必须在对o加锁的同步代码块里
t2会从o的等待队列中释放一个线程/所有线程
推荐:尽量用notifyAll取代notify, 因为由OS决定只释放哪个线程
wait() 和 sleep()的联系和区别:
1. wait()是从Object继承下来的方法,而sleep()是Thread中的静态方法
2. wait() 和 sleep()都要阻塞运行,释放CPU资源
3. wait()要释放锁; 而sleep()不释放锁
wait()方法被调用时会解除锁定,但是我们能使用它的地方只是在一个同步方法或代码块内。
新思路:必要时可利用Object中的 锁标记、锁池、等待队列。 用其wait() / notify() 方法,
自己制造临界资源来进行线程间通讯。
(参考字母数字交叉打印的例子TestNumberCharPrint.java)
其中注意o.notifyAll() 和 o.wait() 的调用顺序,还要注意边界问题,体现在最后要来一个判断if(c!=’Z’) o.wait(); 进而让程序正常结束。
I/O流
一、流的分类
(1) 按数据传输方向划分:输入流和输出流
I 输入流 JVM ——>数据源 InputXXX
O 输出流 JVM <—— 数据源 OutputXXX
(2) 按数据单位划分:字节流和字符流
字节流 一次传输一个字节
字符流 一次传输一个字符
字节流类:
抽象父类: InputStream 字节输入流 xxxInputStream
实现类:
BufferedInputStream 缓冲流——过滤流
ByteArrayInputStream 字节数组流——节点流
DataInputStream 处理Java标准数据流——过滤流
FileInputStream 处理文件IO流——节点流
FilterInputStream 实现过滤流——字节过滤流父类
PipedInputStream 管道流,在两个线程交换数据
PrintStream 包含print() 和 println()
RandomAccessFile 只支持随机访问文件,用于定位的seek()方法。 了解即可
SequenceInputStream 在合并两个文件是较好用(顺序流)
字符流:
可以解决字符编码问题
Reader和Writer (字符流类,所有字符流的父类型)
(3)按流的功能划分:
节点流 / 过滤流(使用装饰模式)
节点流用来传输数据。
过滤流用来封装节点流或者其他过滤流,从而给节点流或其他的过滤流增加功能。
首先要通过构造得到InputStream 或 OutputStream 对象
关闭流时只需关最外层的流。
注意使用out.flush()来清空缓冲区。 而out.close()默认调用flush
I/O本身是抽象的,当 流、源和目的确定了I/O就不抽象了。
u 构造方法的记忆方法:
(1)、代表源的事物,构造方法就加源
例如 FileInputStream File代表源的事物,构造方法就加源
FeInputStreamil
(File file)
(2)、代表功能,构造方法就放InputStream 或 OutputStream
例如 ObjectInputStream Object代表功能,构造方法就放InputStream
DataInputStream Data分为int、byte、char等8种简单类型外加String,
构造方法放InputStream
PushbackInputStream Pushback代表具有缓冲的功能,所以构造方法放InputStream
InputStream, OutputStream为抽象的,子类型不抽象
建议主要记Read / Write 方法即可,其它的掌握规律
比如看到Data就要想到会装不同类型数据: int、byte、char …
InputStream类
所有字节输入流的父类,如:FileInputStream, Ob jectInputStream,PipedInputStrean
三个基本的read()方法
A. int read(): 从流里读出的一个字节或者返回-1;
B. int read(byte[]):将数据读入到字节数组中,并返回所读的字节数;
C. int read(byte[], int p, int len):int参数p表示哪个位置,len表示希望的长度。
FileInputStream类
DataInputStream 类
PipedInputStream类(一般了解)
BuferOutputStream 给流增加一个缓冲的功能,以空间换取时间。 TestBufferedStream.java
JVM |
Data Source |
缓冲区 |
在JVM内部开辟一块缓冲区,提高了读写的效率
根据数据类型选择输入/输出流:
①byte、byte[] InputStream / OutputStream
②int、byte、char DataInputStream / DataOutStream
③char、String Reader / Writer
④Object ObjectInputStream / ObjectOutputStream
若考虑性能会在前试着加Buffered, 在①、③上加; ②则在①的基础上加。
如 FileOutputStream fo=new FileOutputStream(“a.txt”);
BufferedOutputStream out=new BufferedOutputStream(fo);
DataOutputStream dout=new DataOutStream(out);
字符流:
可以解决字符编码问题
每个国家制定编码方式,编码方式、解码方式不同——>乱码问题
ASCII 一个字符-----1B 任何一种编码均兼容 A<-->65
ISO8855-1 (西欧) 一个字符-----1B
GB-2312 / GBK 一个字符-----2B
Unicode 一个字符-----2B 会增加网络负担 Java中的char是Unicode
UTF-8 变长字节(变长的Unicode方式)
英文-----1B
中文-----3B
这里的重点: 字节流——>字符流
InputStreamReader类
OutputStreamWriter类
FileInputSream fi=new FileInputStream(“2.txt”);
InputStreamReader ir=new InputStreamReader(fi,”Big 5” ); //在桥转换时指定编码方式
BufferedReader in=new BufferedReader(ir);
String s;
While((s=in.readLine())!=null){ //readLine()阻塞方法,到换行符为止
System.out.println(s);
}
in.close();
Java EE中用得较多的还是Reader和Writer