概览
- 基础概念
- File
- io阻塞流
- 流相关的概念
- 随机读写
- 网络编程
- PrintStream和PrintWriter的区别
- 字节流读取文本可能存在的问题
- NIO多路复用、同步非阻塞的io
- channel
- Selector
- Buffer
- NIO2
- Path,Paths,Files
- AsynchronousChannel
- NIO与AIO区别
- 同步 异步 阻塞 非阻塞
基础概念
- bit一个二进制位
- byte由8个bit组成你
- char java中是2个byte,16位
- java采用的unicode,包含了ascii编码
File
- File类描述的是一个文件或文件夹。可以获取文件名、创建修改时间、大小、可读可写属性等,同时也可以操作文件及目录
- 判断:exists() isDirectory() isFile()
- 创建:createNewFile() mkdir() mkdirs()这会创建路径中所有不存在的目录
- 删除:delete() deleteOnExit()
- 获取:getAbsolutePath() list()返回所有文件名 listFiles()返回所有File对象
- File.separatorChar 区分操作系统的分隔符,windows为:,linux为/
io阻塞流
流相关的概念
- BufferedReader的作用:是一个处理流,可以将读取到的数据放在内存中
- 按照流的角色划分
- 节点流:从特定的io设备读取流 eg:InputStream。创建的时候要传入数据源
- 处理流:对已经存在的流进行封装处理 eg:InputStreamReader。创建的时候要传入流
- BufferedInputStream的作用:可以减少对硬盘的读取提高处理速度
- Printwriter:如果写的数据种类较多,可以考虑使用Printwriter,提供了很多printWrite的重载方法
- 把我们控制台的输出改成输出到一个文件里面:流的重定向,System.out.setOut(PrintStream)
- 输出字节流转换成输出字符流:New OutputStreamWriter(new FileOutputStream(File file));
- PrintWriter和DataOuputSream的区别:https://www.cnblogs.com/skywang12345/p/io_16.html
- 编码不同:PrintWriter可以用户指定编码,DataOuputSream是utf8
- 构造函数:DataOutputStream只支持OutputStream,PrintWriter支持很多eg file
- 自动刷新:PrintWriter可以指定是否自动刷新
- 目的:DataOutputStream是为了和DataInputStream配合读写java类型的数据,PrintWriter为其他流提供打印的方式
- 异常:PrintStream 永远不会抛出 IOException
- 对象序列化:将对象以二进制的形式保存在硬盘上,对象需要实现serializable,使用ObjectInputStream、ObjectOutputStream
- 如果在对象序列化的时候不想给一个字段的数据保存在硬盘上面:transient
- serialVersionUID字段,它叫做什么,一般有什么用:确保序列化反序列化版本一致
- InputStream:
- read()返回byte
- read(byte[])返回byte数量,同时将数据放入byte[]
- OutputStream:
- write(byte b[], int off, int len)
- 流一旦打开就必须关闭,使用close方法
- 放入finally语句块中(finally 语句一定会执行)
- 调用的处理流就关闭处理流
- 多个流互相调用只关闭最外层的流
按照传输的单元划分流
- 字符流,分为输入和输出
- Reader
- Writer
- 字节流,分为输入和输出
- InputStream
- OutputStream
随机读写
- RandomAccessFile:可以按字节随机读取,构造函数式传入文件名称和读写的权限
- 提供了大量的readxxx writexxx方法,同时可以通过seek()设置读取或者写入点,getFilePointer()获取当前的位置
- 这个是非流操作
网络编程
- tcp:链接顺序
- 创建一个服务器socket
- 客户端发起socket请求
- 服务端启动一个线程处理请求
- 获取io
- 对io进行读写完成交互
- 关闭io
- 关闭socket
- udp:主要用到的类是DatagramSocket、DatagramPacket
参考:https://blog.youkuaiyun.com/baidu_37107022/article/details/76890019
PrintStream和PrintWriter的区别
- 功能是一样的,PrintStream输出采用的是默认的编码格式;PrintWriter可以传入Writer是可以使用OutputStreamWriter(outputStream,charset)来设置字符集
参考: https://blog.youkuaiyun.com/zhhtao89/article/details/50129319
注意事项
- 用字节流读取一个文本文件的时候要特别小心,很有可能只读取了字符的一半
- 先放到ArrayList里,然后转成byte[],再用String(byte[],charset)
- 用ByteArrayOutputStream暂存,然后toString()
https://www.cnblogs.com/JHubery-code/p/6597524.html
NIO
- NIO采用内存映射文件的方式来处理输入输出,将文件的一段映射到内存中
- Channel可以和InputStream、OutputStream类比,不过前者面向缓存区,后者面向流
- 可以将部分数据映射成Buffer
- 创建Channel:
InputStream.getChannel()
- read()
- write()
- map():将部分或者全部数据映射成Buffer,会直接返回一个Buffer
- RamdomAccessFile返回的Chanel可以是既能读又能写的,一般是使用这个RandomAccessFile
- Buffer实际就是一个数组,其他程序不能直接和Channel交互,无论读写Channel都要通过Buffer
XxxBuffer.allocate(capacity)
设置buffer容量- position:下一个可以读取或者写入的位置
- limit:limit后的数字不能读取写入指针的位置,从而灵活读取:position()
- 可以灵活的设置Buffer中
- 切换读写状态
- slip():输入完数据后需要输出数据时调用。切换模式改变limit position的值
- clear():为再次装数据做好准备,但是数据并没有清空
- hasRemaining():判断是否还有数据可读 呆写入
- Selector:
- 单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便
- Selector会不断轮询注册在其上的Channel,如果某个Channel上面发生读或者写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合,进行后续的I/O操作
- SelectionKey:包含interest集合、ready集合(检查哪些操作就绪了)、Channel、Selector、附加的对象(可选)
selector.wakeup()
将堵塞在select()上的线程唤醒
Selector selector = Selector.open();//创建selector
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);//注册channel到selector上
while(true) {
int readyChannels = selector.select();//通过select() selectNow()可以选择就绪的方法,区别主要在是否阻塞,返回有多少通道就绪
if(readyChannels == 0) continue;
Set selectedKeys = selector.selectedKeys();//将就绪的通道取回来
Iterator keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if(key.isAcceptable()) {
// a connection was accepted by a ServerSocketChannel.
} else if (key.isConnectable()) {
// a connection was established with a remote server.
} else if (key.isReadable()) {
// a channel is ready for reading
} else if (key.isWritable()) {
// a channel is ready for writing
}
keyIterator.remove();
}
}
Selector
- 多路复用的实现
- 在读取用户请求数据后(即 selectionKey.isReadable() 为 true),会去解析请求头,然后将请求头信息通过 attach 方法放入 selectionKey 中。待通道可写后,再从 selectionKey 中取出请求头,并根据请求头回复客户端不同的消息。
- 也就是说,通道注册时,并不会立即调用 epoll_ctl,而是先将事件集合 events 存放在 eventsLow
- 选择过程:
- 检查已取消键集合 cancelledKeys 是否为空,不为空则将 cancelledKeys 的键从 keys 和 selectedKeys 中移除,将键和通道注销。
- 调用操作系统的 epoll_ctl 函数将通道感兴趣的事件注册到 epoll 实例中
- 调用操作系统的 epoll_wait 函数监听事件
- 再次执行步骤1
- 更新 selectedKeys 集合,并返回就绪通道数量
- 注册通道实际上只是先将事件收集起来,等调用 select 方法时,在一起通过 epoll_ctl 函数将事件注册到 epoll 实例中。
https://yq.aliyun.com/articles/604966?spm=a2c4e.11163080.searchblog.39.17252ec1Cv5LMB
linux
- epoll_ctl
- epoll
- epoll_wait
https://www.cnblogs.com/Anker/p/3263780.html
nio2
Path,Paths,Files
- 对nio的功能进行了封装,简化编程
- Path接口表示的是一个与平台无关的路径(文件和目录都用Path表示)
- Paths创建Path
- Files,之前的File并不能操作文件,这里通过整合就io,简化了流操作。Files可以操作文件,读写文件,判断文件。
https://www.cnblogs.com/fysola/p/6143939.html
AsynchronousChannel
- 读完了再通知我
- 使用回调函数,进行业务处理
- 实现真正的异步编程
- 可以通过返回Future的方式来获取数据,非阻塞异步
public abstract Future<Integer> read(ByteBuffer dst, long position);
- 也可以通过回调的方式来获取数据
public abstract <A> void read(ByteBuffer dst,
long position,
A attachment,
CompletionHandler<Integer,? super A> handler);
https://blog.youkuaiyun.com/sinat_32366329/article/details/80564338
NIO与AIO区别
- NIO是同步非阻塞的,AIO是异步非阻塞的
- 由于NIO的读写过程依然在应用线程里完成,所以对于那些读写过程时间长的,NIO就不太适合。而AIO的读写过程完成后才被通知,所以AIO能够胜任那些重量级,读写过程长的任务。
charset
Charset c=Charset.forName("字符集名称")
Charset.newDecoder()
获得解码器Charset.newEncoder()
获得编码器- 可以用编码器和解码器做CharBuffer和ByteBuffer转换
文件锁
- 阻止并发修改同一个文件
- Channel.lock()(如果不成功则阻塞)或者Channel.tryLock()(如果不成功返回null),给文件加锁同时返回FileLock
- Channel.lock(position,size,isShare)
- 共享锁:允许多个进程读,但是阻止排他锁获取
- 排他锁:锁定对文件的读写
- FileLock.release()释放锁
socket channel
- 创建链接
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://jenkov.com",80));
- 读取数据
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);
后面有个实例:https://blog.youkuaiyun.com/huangwenyi1010/article/details/75577091?ref=myread
NIO与AIO区别
- NIO是同步非阻塞的,AIO是异步非阻塞的
- 由于NIO的读写过程依然在应用线程里完成,所以对于那些读写过程时间长的,NIO就不太适合。而AIO的读写过程完成后才被通知,所以AIO能够胜任那些重量级,读写过程长的任务。
同步 异步 阻塞 非阻塞
消息通信的机制
- 同步:发出请求在没有得到结果前,不返回
- 异步:调用发出后直接返回,没有返回结果。被调用者通过通知、回调函数通知调用者。操作系统内核读写
等待调用结果时的状态,请求能否立刻应答
- 阻塞:等待结果返回期间,线程挂起
- 非阻塞(非阻塞忙轮询):等待结果返回期间,不阻塞当前的线程。但是需要应用不断查询结果
同步和异步针对应用程序来,关注的是程序中间的协作关系;阻塞与非阻塞更关注的是单个进程的执行状态。