BIO 一对一 同步阻塞
client客户端 | server服务端 |
---|---|
通过socket对象请求服务端 | 通过serverSocket注册端口 |
从Socket中输入或输出 字节流 进行读写 | 调用accept方法监听socket请求 |
从soket 获取 字节 流 读写操作 |
案列
public static void main(String[] args) {
try {
//创建套接字发送请求
Socket socket = new Socket("127.0.0.1",9999);
//获取 字节 输出流
OutputStream os = socket.getOutputStream();
//包装成打印流
PrintStream ps = new PrintStream(os);
//发送一行消息
ps.print("长日将尽");
//刷新容器
ps.flush();
} catch ( Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println("+++ 启动+++");
try {
//创建 套接字
ServerSocket ss = new ServerSocket(9999);
//调用监听
Socket socket = ss.accept();
//获取字节输入流
InputStream is = socket.getInputStream();
//获取一个缓冲 输入流 -> 需要先转型
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//输出内容
if ( br.readLine() != null ) {
System.out.println("服务端接收数据->"+br.readLine());
}
} catch (Exception e) {
e.printStackTrace();
}
}
NIO 异步非阻塞
- Channels
- Buffers
- Selectors
核心API构成。此外Pipe,FileLock ,工具类
Channel 通道
stream 是单向的
Channel 是双向的(inputstream,ouputstream)可以读,也可写
NIO主要实现类:FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel。这里分别对应,文件IO、UDP、TCP(Server和Client)
- FileChannel -->从文件中读写数据
- DatagramChannel -->通过UDP读写网络中都数据
- SocketChannel --> 通过TCP读写网络中的数据
- ServerSocketChannel -->可以监听新进来的TCP连接,类似Web服务器,对每一个新进来的连接都会创建一个 SocketChannel
channel实现是依赖缓冲区的(Buffer)
不会将字节直接写入通道中,相反,是将数据写入包含一个或多个字节的缓冲区。
也不会从通道中读取字节,而是将数据从通道读入缓冲区,在从缓冲区去读取这个字节
channel 通道可以异步读写。
通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入
FileChannel
java.nio.channels
return | Method | describe |
---|---|---|
int | read(ByteBuffer dst) | 从Channel中读取数据到 ByteBuffer |
long | read(ByteBuffer[] dets) | 将Channel中的数据“分散”到ByteBuffer[] |
int | Write(ByteBuffer src) | 将ByteBuffer中的数据写入到Channel |
long | write(ByteBuffer[] srcs) | 将ByteBuffer中的数据“聚焦”到Channel |
long | postition() | 获取通道文件位置 |
FileChannel | position(long p) | 设置通道文件位置 |
long | size() | 返回此通道文件到当前大小 |
FileChannel | truncate(long s) | 将此通道的文件截取为给定大小 |
void | force(boolean metaDate) | 强制将所有对此通道的文件更新写入到储存设备中 |
filechannel使用
-
filechannel 是无法直接使用的,需要通过 InputStream、OutputStream、RandomAccessFile 来获取FileChannel 实例
-
写入
-
buf.clear(); buf.put("写入".getBytes()).flip(); channel.write(buf);
-
//创建 filechannel
RandomAccessFile rw = new RandomAccessFile("nio_bio_aio/a/a.TEXT", "rw");
FileChannel channel = rw.getChannel();
//创建buffer
ByteBuffer buf = ByteBuffer.allocate(1024);
//数据读取 -1 读取不到内容
int byteRead = channel.read(buf);
while( byteRead != -1){
System.out.println( byteRead );
buf.flip();
while( buf.hasRemaining() ){
System.out.println( (char) buf.get());
}
buf.clear();
//再次读取
byteRead = channel.read(buf);
}
rw.close();
channel 之间的数据传输 (可以实现文件复制)
transferFrom(ReadableByteChannel src, long position, long count)
public static void main(String[] args) throws IOException {
//transferFrom() 通道之间的数据传输
RandomAccessFile aw = new RandomAccessFile("nio_bio_aio/a/a.TEXT", "rw");
RandomAccessFile bw = new RandomAccessFile("nio_bio_aio/a/b.text", "rw");
FileChannel ca = aw.getChannel();
FileChannel cb = bw.getChannel();
//a 文件里的流传输到 b文件里面
long size = ca.size();
cb.transferFrom(ca,0L,size);
aw.close();
bw.close();
}
transferTo(long position, long count, WritableByteChannel target)
SocketChannel
新的socket通道类可以运行非阻塞模式并且是可选择的,可以激活大程序(网络服务的中间组件)巨大的可伸缩性和灵活性。socket避免大量线程管理的上下文切换的开销。
java.nio.channels.spi.AbstractSelectableChannel
- DatagramChannel
- SocketChannel
- ServSocketChannel
可以使用一个selecto对象来操作 socket通道的就绪(readiness selection)
注意:DatagramChannel 和 SockeyChannel 实现定义读写功能
ServerSocketChannel不实现。此类负责监听传入的连接和创建新的SocketChannel对象,它本身从不传输数据socket和socket通道之间的关系,通道是一个连接I/O服务导管并提供与该服务交互的方法。就某个socket而言,它不会在此实现与之对应socket通道类中socket协议API,而java.net中已经存在的socket通道都可以被大多数协议重复使用。
全部 sockey通道类(DatagramChannel、SocketChannel、ServerSocketChannel)在被实例化时都会创建一个对等的socket对象,这些是我门熟悉的来自java.net类。他们已经被更新以识别通道。对等socket可以通过调用socket()方法从一个通道上获取此外,这个三个 java.net类现在都**getChannel()**方法
要把一个sockey通置于非阻塞模式,需要依靠所有的socket通道类的共有超级累:SelectableChannel。就绪选择(readiness selection)是一种可以用来查询通道的机制,该查询可以判断通道是否准备好执行一个目标操作,如读或写。非组赛I/O和可选择性是紧密相连的,那夜正是管理组赛模式的APi代码要在 SelectableChannel超级类中定义的原因。
调用 configureBlocking(),传参数为ture 设置为阻赛模式,false非阻塞。
isBlocking()来获取 sockey通道的模式
ServerSocketChannel
基于通道的socket监听器。和java.net.ServerSocket执行相同的任务,不过它增加了通道语义,因此能够在非阻赛模式下运行。
ServerSocketChannel中没有 bind()方法,因此需要取出对等的socket并使用它来绑定到一个端口以开始监听连接。通过使用对等的ServerSocket的API来根据需要设置其他的socket选项
ServerSocketChannel中有accept()方法,一旦创建一个ServerSocketChannel并用对等socket绑定了它,然后您就可以在其中一个上调用accept(),
- ServerSocket.accept() 调用,总是阻赛返回一个 java.net.Socket对象
- ServerSocketChannel.accept() 调用 会返回SocketChannel类型 对象,返回对象可以在非阻赛模式下运行
当ServerSocketChannel以非阻赛模式调用时,没有等待连接传入,调用 accept()会返回null
int port = 9999;
//buffer
ByteBuffer buffer = ByteBuffer.wrap("心若自由,身沐长风".getBytes());
//serverSocketChannel 获取
ServerSocketChannel ssc = ServerSocketChannel.open();
//绑定监听
ssc.socket().bind(new InetSocketAddress(port));
//设置非组赛
ssc.configureBlocking(false);
//监听是否有连接传入
while (true) {
System.out.println(" 监听连接汇总");
SocketChannel sc = ssc.accept();//连接进来 获取一个 单独socket
if (sc == null) {
System.out.println("null");
Thread.sleep(5000);
}else {
System.out.println( "🔗" + sc.socket().getRemoteSocketAddress());//获取连接对接口
buffer.rewind();//指针0
sc.write(buffer);
sc.close();//关闭单独的socket
}
}
SocketChannel
Java.nio 中一个连接到TCP网络的套接字通道,是一种面向流连接的sockets套接字的可以选择通道。
- SocketChannel是用来连接Socket套接字
- SocketChannle主要用途用来处理网络I/O通道
- SokcetChannel是基于TCP连接传输
- SocketChannel实现了可选择通道,可以被多路复用
- 已经存在的socket不能创建socketChannel
- socketChannel中提供的open接口创建的Channel并没有进行网络级联,需要使用connect接口连接到指定地址
- 未进行socketChannle执行I/O操作时,会抛出 NotYetConnectedExceptin
- SocketChannel支持两种I/O模式i,阻赛式和非阻赛式
- SocketChannel支持异步关闭。如果SocketChannel在一个线程上read阻赛,另一个线程对该SocketChannel调用shutdownlnput,则读阻赛的线程返回-1表示没有读取到任何数据;如果SocketChannel调用shutdownWrite在一个线程上 write阻塞会 AsynchronousCloseException
- SocketChannel支持设定参数
- SO_SNDBUF 套接字发送缓冲区大小
- SO_RCVBUF 套接字接收缓冲区大小
- SO_KEEPALIVE 保活连接
- O_REUSEADDR 复用地址
- SO_LINGER 有数据传输时延缓关闭Channel(非组赛模式有用)
- TCP_NODELAY 禁用 Nagle算法
方法 | 描述 |
---|---|
isOpen() | 是否为open状态 |
isConnected() | 是否被连接 |
isConnectionPending() | 是否正在进行连接 |
finishConnect() | 校验正在进行套接字连接SocketChannel是否已经完成连接 |
configureBlocking() | fasle表示非阻赛 |
//获取sokcet
SocketChannel sc = SocketChannel.open(new InetSocketAddress("www.baidu.com", 80));
//设置非阻塞
sc.configureBlocking(false);
//读写操作
ByteBuffer bb = ByteBuffer.allocate(16);//allocate 缓冲区的数组长度
sc.read(bb);
sc.close();
System.out.println("非阻塞状态能打印");
DatagramChannel
每一个DataGramChannel对象夜又一个关联的DatagramSocket对象,
DatagramChannel模拟包导向的无连接协议(UDP/IP)。每个数据报(datagram)都是一个自包含的实体,拥有它自己的目的地址及不依赖其他数据报的数据负载。与面向流的socket不同,DatagramChannel可以发送单独的数据报给不同的地址。同样,DatagramChannel对象也可以接受来自任意地址的数据包,每个到达的数据报都包含有关它来自何处的信息(源地址)
发送
- 创建 DatagramChannlel.open()
- 调用 send(). 绑定 目标 inetSocketAddress
//发送实现
static void sendDatagram() throws IOException, InterruptedException {
DatagramChannel sc = DatagramChannel.open();
InetSocketAddress sadd = new InetSocketAddress("127.0.0.1", 9999);
//发送
while (true) {
ByteBuffer buff = ByteBuffer.wrap("数据到达".getBytes("UTF-8"));
sc.send(buff, sadd);
System.out.println("数据发出");
Thread.sleep(2000);
}
}
接收
- 创建 DatagrmChannel.open()
- 监听 bind() inetSockeAddress 端口
//接收端实现
static void receiveDatagram() throws IOException {
DatagramChannel dc = DatagramChannel.open();
InetSocketAddress ia = new InetSocketAddress(9999);
//绑定
dc.bind(ia);
ByteBuffer bye = ByteBuffer.allocate(1024);
//接收
while (true) {
bye.clear();
//获取地址
SocketAddress socketAddress = dc.receive(bye);
//反转
bye.flip();
System.out.println(socketAddress.toString() );
System.out.println(Charset.forName("UTF-8").decode(bye) );
}
}
read / wrier
// read write
static void testConnedct() throws IOException {
DatagramChannel dc = DatagramChannel.open();
//绑定
dc.bind( new InetSocketAddress(9999));
//连接
dc.connect(new InetSocketAddress("127.0.0.1", 9999));
dc.write(ByteBuffer.wrap("发送目标的数据".getBytes("UTF-8")));
ByteBuffer bye = ByteBuffer.allocate(1024);
while (true ){
bye.clear();
dc.read(bye);
bye.flip();
System.out.println( Charset.forName("UTF-8").decode(bye) );
}
}
Scatter / Gather 分散/ 聚集
Java.nio 中支持 scatter/gether 来描述 channel中读取或者写入到channel的操作
分散 scatter 从 Channel中读取是指在读操作时将读取的数据写入多个buffer中,Channel从Channel中读取的数据“分散 scatter ”到多个Buffer
聚集 gether写入Channel是指在写操作时将多个buffer的数据写入同一个Channel,因此,Channel将多个Buffer中的数据“聚集 gather” 后发送到Channel。
分散聚合经常用于需要将传输的数据 分开处理的场合,例如传输一个有消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的buffer中。
分散过程中,buffer被插入到数组,然后再将数组作为channel.read()的输入参数
read()按照buffer在数组中的顺序将从channel中读取的数据写入到buffer,当一个buffer被写满后,channel 紧接着向另一个buffer中写。
Scattering Reads 在移动下一个buffer前,必须填满当前的buffer,这也意味着它不是用与动态消息(消息大小不固定)。如果存消息头和消息体,消息头必须完成填充(例如:128byte),Scattering Reads 才能正常工作
Buffer 缓冲区
NIO中关键Buffer实现:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer
NIO中的所有数据基本都是通过buffer处理的,buffer本质是数组
基本使用
- 写入数据 到 buffer
- 反转 flip()
- 读数据
- 清空。clear() 清空整个缓冲区。 compact() 清除已经读过的数据,未读数据移动到缓冲区的起始处。 新的数据接着后面写入
xxxBuffer.allocate(1024);//分配 1024的容量
Channel.read(buffer);//通过chaanel 写入数据
buffer.put( data );//直接put 写数据
Chaaenl.write( buffer );// channel 读
buffer.get();//直接读
当向buffer写入数据时,buffer会记录写了多少数据,一旦读取数据,需要通过flip 方法将buffer从写模式切换读模式。读完之前写的数据,需要清空缓冲区,恢复到能写入clear、compact
buffer 属性 Capacity position limit
capacity 的含义一直不变
position 和 limit 的含义取决于buffer处在读写模式。
capacity
内存块,buffer有一个固定的大小值,也叫‘capacity’你只能往里写capacity 个byte,long,char 等类型。一旦buffer满了,需要将其清空(通过读数据或者清空数据)才能继续写入数据
position
- 写模式:position 表示写入数据的当前位置,position的初始值为0。当一个byte、long等数据写到Buffer后,position会向下移动到下一个可插入的数据Buffer单元。position最大可为capacity -1 (因为position的初始值为 0)
- 读模式: position表示读入数据的当前位置,如position = 2时表示以及开始读入 3个 byte ,或者从第3个byte开始读取。通过bytebuffer.flip()切换到读模式时,position会被重置为0,当buffer从position读如数据后,position会一到下一个可读入的数据buffer单元
limit
- 写模式: limit表示可对buffer最多写入多少个数据。写模式下,limit等于buffer的capacity
- 读模式: limit表示buffer里有多少可读数据(not null 的数据),因此能之前写入的所有数据(limit 被设置成以写数据的数量,这个值在写模式就是position)
缓冲区操作
缓冲区分片
在NIO中,除了可以分配或者包装一个缓冲区对象外,还可以根据现有的缓冲区对象来创建一个子缓冲区,即在现有缓冲区上切处一片来作为一个新的缓冲区,但现有的缓冲区来创建子缓冲区在底层数组层面上时数据共享的,也就是说,子缓冲区相当于现有缓冲区的一个视图窗口。调用slice()方法可以创建一个子缓冲区。
static void slice0(){
ByteBuffer bf = ByteBuffer.allocate(10);
for (int i = 0; i < 10; i++) {
bf.put((byte)i);
}
//创建子缓冲区 3-7的位子
bf.limit(7);
bf.position(3);
ByteBuffer slice = bf.slice();
//操作子缓冲区
for (int i = 0; i < slice.capacity(); i++) {
byte b = slice.get(i);
slice.put(i,b*=10);
}
//恢复 属性
bf.position(0);
bf.limit(bf.capacity());
while ( bf.remaining() > 0) System.out.print( bf.get()+" " );
}
结果: 0 1 2 30 40 50 60 7 8 9
目前的分片缓冲区就是 移动 limit 和 position 的临时区域
只读缓冲区
只读缓区可以读取它们,但不能向它们写入数据,可以通过调用缓冲区的asReadOnlyBuffer()方法,将任何常规缓冲区转换为制度缓冲区,这个方法返回一个与原缓冲区完全相同的缓冲区,并与源缓冲区共享数据,只不过它时只读的。原缓冲区发生变化,只读缓冲区的内容也随之发生变化。
static void read01() {
ByteBuffer buffer = ByteBuffer.allocate(10);
for (int i = 0; i < buffer.capacity(); i++) {
buffer.put((byte)i);
}
//创建只读缓冲区
ByteBuffer readonly = buffer.asReadOnlyBuffer();
for (int i = 0; i < buffer.capacity(); i++) {
byte b = buffer.get(i);
b *=10;
buffer.put(i,b);
}
readonly.position(0);
readonly.limit(buffer.capacity());
while (readonly.remaining()>0) {
System.out.println(readonly.get());
}
}
直接缓冲区
直接缓冲区时为了加快I/O速度,使用一种特殊的方式为其分配内存的缓冲区,JDK文档中描述:给定一个直接字节缓冲区,java虚拟机将尽最大努力直接对它执行本机I/O操作,每次调用底层操作系统的本机I/O操作之前,尝试避免将缓冲区的内容拷贝到一个中间缓冲区或者一个中间缓冲区中拷贝数据,分配直接缓冲区,使用 allocateDirect()
内存映射文件I/O
内存映射文件I/O是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的I/O快的多,内存映射文件I/O是通过使文件中的数据出现为内存数组内容来完成的,这并没有把整个文件读到内存中,只有文件实际读和写的部分才会映射到内存中。速度比流 和通道更快
static void mapped() throws IOException {
RandomAccessFile rw = new RandomAccessFile("nio_bio_aio/a", "rw");
FileChannel fc = rw.getChannel();
MappedByteBuffer map = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
map.put(0,(byte)97);
rw.close();
}
Selector 选择器
Selector 运行单线程处理多个Channel,如果你的应用打开了多个通道,单每个连接的流量都很低,使用selector就会很方便,
seletableChannel 可选通道复用
- 只有继承了 selectableChannel的类才可以复用
- selectableChannel提供了通道可选择性所需要的公共方法,所有支持就绪检查的通道类的父类,socket通道,都继承了selectableChannel都可以选择,从过包括管道(Pipe)对象的中获得通道,而FileChannle类没没继承SelectableChannel,是不可选通道
Channel 注册到 selector
-
使用channel.register(Selector sel,int ops) 将一个通道注册到一个选择器
- Selector: 指定通道需要注册到选择器
- int ops :指定选择器需要查询的通道操作
-
提供选择查询的通道操作,从类型分为四中
- 可读: SelectionKey.OP_READ
- 可写: SelectionKey.OP_WRITE
- 连接: SelectionKey.OP_CONNECT
- 接收: SelectionKey.OP_ACCEPT
selector也可以用 位或 操作符来实现 例如:int key = SelectionKey.OP_READ | SelectionKey.OP_WRITE
-
选择器查询的不是通道的操作,而是通道的某个操作的一种就绪状态。通道一旦具备完成某个操作的条件,就表示通道已经就绪,可以被selector查询到,程序可以对通道进行对应的操作。比方说,某个socketChannel通道可以连接到一个服务器,则处于“连接就绪状态”(OP_CONNECT).再比方说,一个serverSocketChannel服务器通道准备好接收新的连接,“接收就绪状态”(OP_AC CEPT)状态,可读数据通道“读就绪”(OP_READ),“写就绪”(OP_WRITE)
选择键 SelectionKey
- Channel注册后,通道处于就绪状态,就可以被选择器查询到,这个是使用 **选择器Selector 到 select()**进行查询
- select可以不断查询Channel中发生的操作的就绪状态,并且挑感兴趣的操作就绪状态,一旦通道满足就绪条件,就放入选择键 集合中
- 一个选择键,首先是包含了注册在selector的通道操作的类型,如OP_READ,包含了特定通道与选择键之间的注册关系。 NIO编程 ,就是根据对应的选择键,精心不同的业务逻辑处理
使用Selector时,Channel必须非阻塞模式,否则抛出IllegalBlockingModeExcepion(FileChannel 没有非阻塞模式)
channel通validOps()方法,获取特定通道下所支持的操作集合
selector通过slect(),查询出已经就绪的 channel,存在一个元素 selectionkey对象 set集中
选择器执行过程,系统底层会一次询问每个通道是否已经就绪,这个过程可能会造成调用线程进入阻赛模式
wakeup(),阻赛状态的select() 立刻返回
colse(), 关闭 selector :注销 selector中的所有channel,channel本身不会关闭
例子
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
//设置非阻赛
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(9999));
//通道注册到选择器上
ssc.register(selector, SelectionKey.OP_ACCEPT);
//查询就绪通道
Set<SelectionKey> selectionKeys = selector.selectedKeys();
//遍历集合
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
//判断状态 isAcceptable 、 isConnectable 、 isReadable 、 isWritable
if( key.isAcceptable() ){}
iterator.remove();
}
}
Pipe 管道。fileLock
Pipe
两个线程之间的单向数据连接,pipe有一个 source和一个sink通道,数据会被写到sink通道,从source通道读取
Pipe pipe = Pipe.open();//打开通道
Pipe.SinkChannel sinkChannel = pipe.sink();//需要访问的通道
sinkChannel.write("写入数据");
sinkChannel.read("读取数据");
FileLock
文件锁,在多个程序访问、修改同一个文件,会因为文件不同而出现问题,给文件加个锁,同时只能有一个程序修改此文件,或者程序都只能读取此文件。
文件锁是进程级别,不是线程级别。文件锁可以解决多个进程并发访问、修改。但不能解决多个线程并发访问、修改。
文件锁是当前程序所属的JVM实例持有,一旦获取到文件锁,需要调用release(),或者关闭对应的 FileChannel对象,或者当前JVM退出,才会释放这个锁
一个进程对某个文件加锁,则在释放这个锁之前,次进程benign在对次文件加锁,JVM实例在同一个文件上的文件锁是不重叠的。
文件锁: 排他锁,共享锁
FileChannel fileChannel = new FileOutputStream("源文件").getChannel();
//上锁 排他锁 阻塞其他线程
FileLock lock = fileChannel.lock();
// fileChannel.tryLock(); 非阻赛,其他线程获取不到锁 返回null
lock.relesase(); //解锁
锁判断方法
fileLock.isShared() ;//文件是否是共享锁
fileLock.isValid();//文件锁是否还有效
Path 接口
是java7 更新到 java.nio.file 包中的
path表示文件系统中的路径,一个路径可以指向一个文件或一个目录。路径可以是绝对路径,也可以是相对路径。
在许多方面 java.niofile.path 接口类似于 java.io.file类,但有一些差别,不过在许多情况下,可以使用Path接口来替换File类
Path path = Paths.get("文件绝对路径");//通过实例工厂创建
Paths.get("C:/绝对/路径","a/a.text");// C:/绝对/路径/a/a.text
Files 类
java.nio.file.files
提供了几种操作文件系统中的文件方法,
方法 | 描述 |
---|---|
createDirectory() | 根据Path实例创建一个新目录–>目录存在就抛出异常 |
copy() | 从一个路径拷贝一个文件到另一个目录 |
move() | 移动一个文件 |
walkFileTree() | 递归遍历目录 |
遍历寻找指定文件
public static void main(String[] args) {
//搜索的目录
Path path = Paths.get("/Applications/0utils/note/project");
//需要寻找的文件
String file1 = File.separator+"c.text";
try {
Files.walkFileTree(path,new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String s = file.toAbsolutePath().toString();
if( s.endsWith(file1) ){
//匹配到 文件,输出完整路径
System.out.println( file.toAbsolutePath() );
return FileVisitResult.TERMINATE;
}
//没匹配到就继续
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
AsynchronousFileChannel 异步文件操作
读取
//读取文件
static void readAsyncFileChannelFuture() throws IOException {
//创建 AsynchronousFileChannel
Path path = Paths.get("nio_bio_aio/a/b.text");
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
ByteBuffer allocate = ByteBuffer.allocate(1024);
Future<Integer> future = fileChannel.read(allocate, 0);
//判断异步是否完成 等待结束
while(! future.isDone());
//读取数据到buffer
allocate.flip();
byte[] data = new byte[allocate.limit()];
allocate.get(data);
System.out.println( new String(data));
allocate.clear();
}
写入
默认覆盖的的方式,按字节覆盖
static void writeAsyncFileFunture() throws IOException {
//创建 AsynchronousFileChannel
Path path = Paths.get("nio_bio_aio/a/c.text");
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
ByteBuffer byt = ByteBuffer.allocate(1024);
byt.put("浊酒".getBytes());
byt.flip();
Future<Integer> future = fileChannel.write(byt, 0);
//等待异步完成
while (! future.isDone());
byt.clear();
}
ChartSet 字符集
java.nio.charset.Charset
表示字符集编码对象
常用方法
方法 | 描述 |
---|---|
forName(String charsetName) | 通过编码类型获取charset对象 |
isSupported(String charsetName) | 判断是否支持编码类型 |
availableCharsets() | 获取系统支持的所有编码方式 |
defaultCharsert() | 获得虚拟机默认编码方式 |
name() | 获取当前对象编码类型 |
newEncoder() | 获取编码器对象 |
newDecoder() | 获取解码器对象 |
public static void main(String[] args) throws CharacterCodingException {
//获取对象
Charset charset = Charset.forName("UTF-8");
//获取编码器
CharsetEncoder charsetEncoder = charset.newEncoder();
//解码器
CharsetDecoder charsetDecoder = charset.newDecoder();
//数据
CharBuffer charbuff = CharBuffer.allocate(1024);
charbuff.put("半醉半醒").flip();
//编码结果 -27 -115 -118 -23 -122 -119 -27 -115 -118 -23 -122 -110
ByteBuffer byteBuffer = charsetEncoder.encode(charbuff);
// byteBuffer.flip();
//解码 TODO
CharBuffer charBuffer1 = charsetDecoder.decode(byteBuffer);
System.out.println( charBuffer1.toString() );//半醉半醒
//遍历对象
// for (int i = 0; i < byteBuffer.limit(); i++) {
// System.out.print( byteBuffer.get() +" ");
// }
}