core java 10~12(多线程 & I/O & Network网络编程)

时间:2023-11-26 11:45:38

MODULE 10 Threads 多线程
--------------------------------

进程: 计算机在运行过程中的任务单元,CPU在一个时间点上只能执行一个进程,但在一个时间段上采用时间分片原则。

特点:
每个进程执行时需要独立的数据空间,独立分配内存,多个进程间的资源互不共享。因此进程是非常耗费资源的

线程:是程序运行时的最小执行单位

特点:
1)线程依赖于进程
2)一个进程可以分为多个线程 例:QQ
3)*多线程可以共享资源

多进程和多线程的设计目的都是为了在同时执行多个任务

多线程带来的问题:
多线程间的并发(互斥)与同步,(由共享资源引发的问题)

线程类Thread

创建自己的线程
1.继承Thread类
class MyThread extends Thread{
public void run(){
//线程处理代码
}
}
运用:
MyThread th1=new MyThread();
th1.run();//错误
th1.start();//启动该线程,执行的是run()方法中的代码
2.实现Runnable接口
class MyThread implements Runnable{
public void run(){
//线程处理代码
}
}
运用:
MyThread th1=new MyThread();
th1.start();//错误
Thread thread=new Thread(th1);
thread.start();//启动线程,执行的是th1中的run()方法

练习:
龟兔猫狗百米赛跑,要求:只要有一个先跑到终点,比赛结束

分析:设置一个多个线程都共享的数据
static boolean isRunning=true;

线程生命周期的状态:
1.Runnable就绪状态
new -->start() --> Runnable
等待JVM调度执行
2.Running状态
JVM采取时间分片策略,被调度的线程从Runnable进入Running状态
执行run()方法中的代码,一旦时间片结束,无论run()方法是否执行完,JVM都会收回该线程的执行权,调度其他处于Runnable状态的线程
Runnable <-----> Running

3.Dead死亡状态
run()方法执行结束

4.Blocked阻塞状态
1)sleep() ---> blocked
等到睡眠时间结束,该线程从bloced ---> Runnable,等待JVM重新调度
2)join() ---> blocked
调用另一线程的join()方法,使本线程进入阻塞状态,直到另一线程执行结束,本线程 blocked ---> Runnable

多线程的并发访问
----------------------------------
多个线程共享数据引发并发问题

练习:

如何解决并发访问?
synchronized关键字
1)寻找公有的对象,并在公有对象身上加锁,确保一次只能有一个线程操作该对象
synchronized(Object){
//只允许一个线程执行这部分代码
}
2)精确控制加锁的范围(或者说加锁的临界值),否则影响程序效率

线程如何从locked回到Runnable状态
------------------------------------
1)sleep 睡眠时间结束,回到Runnable
2)join() 等到另一线程执行结束,本线程回到Runnable
3)如何人为使线程从blocked ------> Runnable
a) interrupt()
人为使线程从blocked ------> Runnable
阻塞的线程一旦被调用interrupt()方法唤醒,会抛出InterruptedException,线程自己可以通过捕获该异常知道自己被中断
b)isInterrupted() 返回boolean类型,判断线程是否被中断
c)interrupted() 清除中断信息,因为一个线程被中断后会设置标志信息,isInterruped()判断为true,一旦调用interrupted()清除中断信息后,isInterruped()判断改为false

练习:ThreadState.java , 在主线程中监控另一个线程让该线程sleep后,调用interrupt()方法使其从blocked回到Runnable

线程同步问题
-------------------------------------
并发:是多个线程地位均等,共同竞争对公有对象的访问权,一次只能一个线程访问

同步:解决的是多个线程对公有对象访问的先后顺序问题

解决同步问题的思路:
1)找出公有对象,多个线程都通过公有对象进行通信
2)找出哪个线程该wait,哪个线程负责notify发通知
3)确保wait在notify之前
添加标志变量hasWait,等待的线程在调用wait()之前设置该变量,负责发通知的线程在notify()之前先判断该变量,确保notify时公有对象身上已经有wait的线程

练习:
定义一个线程CalculateThread,计算1~100的和,把结果放到一个对象中
定义一个线程PrintThread,从该对象中取出结果并打印输出
定义一个测试程序Test.java

分析:
1)属于同步问题
2)公有对象Result{int value;}
3)CalculateThread负责notify,PrintThread负责wait

注意点:
1.wait()和notify()要加synchronized
多个线程竞争公有对象res的访问锁,等待的线程间要互斥
2.等待的线程在wait之前要先设置标志变量,否则wait之后阻塞,无法设置
3.发通知的线程在notify之前要循环判定公有对象身上是否有等待的线程,有wait线程才发通知唤醒
4.while循环一定要放synchronized外面,否则拿着锁再sleep,其他线程永远拿不到res对象的访问权

setPriority() 设置线程的优先级 1~10级 优先级并不能决定线程的先后执行顺序,最终仍由JVM决定

Thread.yield() 将执行权让给优先级比自己高的线程

弃而不用的方法:
stop() / resume() / suspend()
这些方法被调用时,线程所占据的资源的锁不会被释放

死锁问题
------------------------------
若干线程去竞争有限的资源,要求同时拿到多个公有资源时,因公有资源数量有限,不能满足所有线程的需求,每个线程都只拿到部分资源且不释放,就造成死锁问题

哲学家就餐问题

如何解决?
让多个竞争资源的线程以相同的顺序去拿

MODULE 11 I/O 输入/输出
------------------------------------
java中采取流的概念, 在应用程序和外围设备之间建立一个流对象,确保无论外围设备是什么,应用程序只单一的通过访问流对象来读写数据

按流的方向,分为:
输入流InputStream
程序从输入流【读取】数据,但不能写入
read()...
输出流OutputStream
程序往输出流中【写入】数据,但不能读取
write()...

按照流的传输单位
字节流
以字节为基本传输单位,一个个字节传输
注意点:
1)流中的数据没有结构,例int数据4个字节,拆成4次传输
2)最终传输给外围设备的一定是字节流
通常以Stream结尾的都是字节流*

字符流
字节的可读性很差,字符流提供以文本的形式读写数据
通常带有Reader/Writer的都是字符流*

具有缓存功能的流
在流对象中设置缓存区,数据先放入缓存区,再一次性读写外围设备
目的:提高数据的传输性能

过滤器
对流中提供的数据进行进一步处理
字节流传输无结构,过滤器可以将字节流中的字节拼成应用程序需要的数据类型,如int double float...
*不能单独使用,一定要结合某个字节流

InputStream常见的方法
read() 每次返回一个字节,返回结果>=0为有效数据, -1表示流中数据读完
read(byte[]) 可以一次读取多个字节,放入byte[]数组中,返回一次读取成功的字节数

注意:
I/O流要与外围设备进行交互,所有资源都要手工回收
finally{
//添加资源回收代码
}

close() 关闭流对象,释放内存资源
available() 判断流是否可用

flush() 程序强制将缓存区中的数据刷新到外围设备中

字节流的层级结构
-------------------------
通常InputStream/OutputStream 前面的代表了数据源或数据目的地类型
FileInputStream 把文件作为数据源,从文件中读数据
FileOutputStream 把文件作为数据目的地,往文件中写入数据

PipedInputStream 把管道作为数据源
PipedOutputStream 把管道作为数据目的地

过滤器 对字节流进一步包装
1)BufferedInputStream
具有缓存功能的流
2)PushbackInputStream
把从流中读取的数据退回去
3)DataInputStream
能够将字节流中的字节拼成程序需要的基本数据类型
readInt(0 readDouble()...

包: java.io;
异常: IOException

练习:创建类Copy.java实现文件的拷贝
将一个文件的内容拷贝到另一文件

分析:从源文件读入数据,FileInputStream read()
写入目标文件 FileOutputStream write()

改造:以提高性能的方式

例:以提高性能的方式从指定文件啊src.txt中读取含有基本数据类型的内容

分析:
DataInputStream dis=new DataInputStream(new BufferedInputStream(new FileInputStream("src.txt")));

练习:将一个int型整数写入文件中
1)将一个个字节传输,将int拆成4个字节
2)DataOutputStream(BufferedOutputStream(FileOutputStream))

管道流PipedInputStream/PipedOutputStream
-----------------------------------------------
PipedInputStream 从管道中读取数据
PipedOutputStream 往管道中写入数据
通过管道将输入流和输出流衔接起来

练习:
创建两个线程,通过管道流实现两个线程的数据传输
一个线程Sender负责产生100个随机数,并写入管道中
一个线程Fetcher负责从管道中读取数据,并打印输出

分析; class Sender extends Thread{
PipedOutputStream pos;
public void run(){}
}
class Fetcher extends Thread{
PipedInputStream pis;
public void run(){}
}

字符流Reader/Writer
------------------------------
字节流可读性差,字符流提供以文本形式传输数据

特别的流对象
1.BufferedrReader/BufferedWriter
1)开辟缓存区提高传输性能
类似BufferedInputStream(BIS)/BOS
2)提供字符流和字符串之间的转换
类似DIS/DOS
BufferedReader: readLine() 字符流--->字符串
BUfferedWriter: write(String,off,len) 字符串---->字符流

2.InputStreamReader/OutputStreamWriter 桥梁类
最终与外围设备交互的是字节流
1)提供字节流和字符流之间的转换
InputStreamReader:字节流-----> 字符流
OutputstreamWriter:字符流----> 字节流
2)提供java标准编码UTF8和其他编码之间的转换

3.FileReader/FileWriter 是InputStreamReader/OutputStreamWriter的子类
1)具有读写文件的功能
2)作为ISR/OSW的子类,也提供字节流和字符流之间的转换
3)将编码自动转换成操作系统对应的编码格式

练习:
将一个字符串写入文件中,再读取该文件并将内容输出到控制台

分析:
字符串 --->文件
字符串----> 字符流 BuffredWriter
字符流 字节流 OutputStreamWriter FileWriter
字节流 文件 FileOutputStream
文件-----> 控制台

System.out 标准输出
System.in 键盘
System.err 标准错误

对象序列化 ObjectInputStream/ObjectOutputStream
------------------------------------------------------------
序列化:将对象转化为字节流,通常用于保存对象 的当前状态信息
使对象持久化,以备将来对该对象进行恢复
反序列化:字节流-----> Object

ObjectInputStream: Object readObject(){} 反序列化
ObjectOutputStream: writeObject(Object) 序列化

序列化实现接口:
Serializable

class Company implements Serializable{
String name;
int tel;
transient Address add; //标注该属性信息无法序列化
}
class Address{
String city;
String street;
int no;
}

注意:
1)大对象中包含小对象,序列化时要求小对象也实现了序列化接口
2)对于不能序列化的属性,需要用transient修饰

练习:ObjectTest.java

RandomAccessFile随机访问文件
-----------------------------------
以随机访问的方式读写文件中任意一段内容

skip(long) 虽是跳步,但也是从头开始跳过若干字节再进行读写

功能:
1)实现了DataInput / DataOutput接口,类似过滤器
2)读/写功能都具有
readInt() writeInt()......
3)具有操作文件的功能
4)可以随意跳到文件的某个位置开始读写

构造器中的参数:
mode 指定读/写的模式
"r":只读 "r":只写 "rw":读/写

seek(long) 跳过long 指定的若干字节数,开始读写

MODULE 12 Network网络编程
--------------------------------
ip地址 通过IP地址可以唯一定位到网络上的某台机器
port端口:人为制造的数字,代表一个服务器上某个应用的唯一标识

基于TCP/IP网络编程
传输控制协议,考虑的是传输的可靠性问题
基于UDP的网络编程
用户数据报文协议,考虑的是传输的效率问题

通讯双方满足的五要素:
1.通讯双方IP地址(两个)
2.通讯双方的PORT端口号(两个)
3.通讯双方要遵守同样的协议

java.net包:
Socket/SeverSocket:实现基于TCP/IP网络编程
DatagramSocket/DatagramPacket:为UDP协议服务

IP网络层
基本特点:
无连接的;数据可靠性不能保证的;

TCP 传输层
1)面向连接的
2)完全可靠的数据传输
3)点对点的
4)同一连接既可以发送也可以接收
5)面向字节流的

连接的简历经过了三次握手:
1)客户端发出连接请求
2)服务器回复确认
3)建立连接

Client:
构建Socket,连接指定的IP和port
---> 获取输入流/输出流
--> 对I/O流进行包装
---->读/写数据
-->释放资源(Socket/I/O)

Server:
构建SeverSocket,指定监听的端口号
--->接收客户端连接请求,获取Socket建立连接
----> 对I/O流进行包装
---->读/写数据
-->释放资源(sevrverSocket/Socket/I/O)

PrintWriter功能
String-->字节流
BufferedWriter 字符串-->字符流
OutputStreamWriter 字符流--->字节流
1.字符串--->字节流
兼具了BufferedWriter/OutputStreamWriter两者的功能
2.可以自动刷新
new PrintWriter(OS,true);

UDP 用户数据报文协议
-------------------------------------------
1)考虑的是数据传输的效率问题,可靠性不保证
2)不一定是一对一,而是多对多通讯,如广播
3)无连接的,不可靠的传输方式

DatagramSocket 负责数据的发送和接受 如邮递员
DatagramPacket 把数据打成报文对象,填入对方的IP地址和端口号 如信件

构造器:
DatagramPacket()