阻塞IO
读写时候阻塞,每个请求是一个线程
非阻塞IO
基于Reactor工作模式,IO不会阻塞,注册特定IO事件,发生特定事件时候系统会通知。核心对象是Selector, 读、写、注册发生时候selector可以找到发生事件的Selector Channel,获取客户端数据。
非阻塞IO事件本身不阻塞,但是获取事件的select()方法是阻塞的,本质是发生io时候,而不是以前只要IO流打开就一直等待。
NIO
核心组件:Channels Buffers Selectors
filechannel
写入文件小例子
public class io1 {
public static void main(String[] args) throws IOException {
File f = new File("C:\\Users\\86158\\Desktop\\datax.txt");
RandomAccessFile ra = new RandomAccessFile(f,"rw");
FileChannel fc = ra.getChannel();
ByteBuffer bf = ByteBuffer.allocate(1024);
String input = "abcdefg";
bf.clear();
bf.put(input.getBytes());
bf.flip();
while (bf.hasRemaining()){
fc.write(bf);
}
fc.close();
}
}
FileChannel position在某些特定位置进行读写 size返回文件大小 truncate 截取字节 force 强制写入 transferTo/transferFrom 通道间数据传输
通道间数据传输小例子
public class io2 {
public static void main(String[] args) throws IOException {
File f = new File("C:\\Users\\86158\\Desktop\\datax.txt");
File f2 = new File("C:\\Users\\86158\\Desktop\\datax2.txt");
RandomAccessFile ra1 = new RandomAccessFile(f,"rw");
RandomAccessFile ra2 = new RandomAccessFile(f2,"rw");
FileChannel fc1 = ra1.getChannel();
FileChannel fc2 = ra1.getChannel();
fc1.transferTo(0,fc1.size(),fc2);
}
}
socketChannel
serverSocketChannel可以监听新进来的TCP连接的通道
监听8888端口
public class io3 {
public static void main(String[] args) throws IOException, InterruptedException {
int port = 8888;
ByteBuffer bf = ByteBuffer.wrap("hello".getBytes());
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(port));
ssc.configureBlocking(false);
while (true){
SocketChannel sc = ssc.accept();
if(sc == null){
Thread.sleep(2000);
}else {
System.out.println(sc.socket().getRemoteSocketAddress());
bf.rewind();
sc.write(bf);
sc.close();
}
}
}
}
socketChannel 面向TCP
public class io4 {
public static void main(String[] args) throws IOException {
SocketChannel sc = SocketChannel.open(new InetSocketAddress("www.baidu.com",80));
sc.configureBlocking(false);
ByteBuffer bf = ByteBuffer.allocate(100);
sc.read(bf);
sc.close();
System.out.println("over");
}
}
datagramChannel 面向UDP
public class io5 {
public void send() throws IOException, InterruptedException {
DatagramChannel dc = DatagramChannel.open();
InetSocketAddress ad = new InetSocketAddress("127.0.0.1",9999);
while (true){
ByteBuffer bf = ByteBuffer.wrap("jsjsjsj".getBytes());
dc.send(bf,ad);
System.out.println("发送");
Thread.sleep(1000);
}
}
public void receive() throws IOException {
DatagramChannel dc = DatagramChannel.open();
InetSocketAddress ad = new InetSocketAddress(9999);
dc.bind(ad);
ByteBuffer bf = ByteBuffer.allocate(1024);
while (true){
bf.clear();
SocketAddress sc = dc.receive(bf);
bf.flip();
System.out.println(sc.toString() + bf);
}
}
}
channel的scatter和gather功能
Buffer
就是一块可以读写的内存空间,NIO中所有数据都用缓冲区处理
两种分配Buffer的方法: ByteBuffer buf = ByteBuffer.allocate(10); 在堆中开辟,易于管理,垃圾回收器可以回收,空间有限,读写慢。
ByteBuffer buf2=ByteBuffer.allocateDirect(10); 物理内存中开辟空间,空间比较大,读写文件速度快,不受垃圾回收器控制。
buffer读写操作
public class io6 {
public static void main(String[] args) throws IOException {
buffer01();
}
public static void buffer01() throws IOException {
RandomAccessFile ra = new RandomAccessFile("C:\\Users\\86158\\Desktop\\datax.txt","rw");
FileChannel channel = ra.getChannel();
ByteBuffer bf = ByteBuffer.allocate(1024);
channel.read(bf);
bf.flip();
while (bf.hasRemaining()){
System.out.println((char) bf.get());
}
bf.clear();
for (int ii = 0; ii < bf.capacity();ii++){
bf.put((byte) ii);
}
bf.flip();
while (bf.hasRemaining()){
System.out.println((char) bf.get());
}
bf.clear();
}
}
buffer的属性 capacity position limit
capacity: 最多能写多少
position: 读写的当前位置,flip后变0
limit : 最多读到哪里(flip前的position)/最多写到哪里(capacity)
buffer放入数据两个方式
buffer.put() channel.read(buffer)
buffer读数据 buffer.get() channel.write(buffer)
rewind方法/flip方法
flip是写好了开始读,limit 要设成position,position变0
rewind是单纯position变0
clear方法/compact方法
clear: position设0 compact:准备好写数据了,未读过的数据不清除,未读数据拷贝到buffer起始处,position设为最后一个未读数据后面
mark方法/reset方法
mark标记特定position, reset恢复到这个position
buffer分片
在 NIO 中,除了可以分配或者包装一个缓冲区对象外,还可以根据现有的缓冲区对象 来创建一个子缓冲区,即在现有缓冲区上切出一片来作为一个新的缓冲区,但现有的 缓冲区与创建的子缓冲区在底层数组层面上是数据共享的,也就是说,子缓冲区相当 于是现有缓冲区的一个视图窗口。调用 slice()方法可以创建一个子缓冲区。
切片范围是调用slice方法时原始缓冲区的position到limit索引之间的数据
buffer只读
只读缓冲区非常简单,可以读取它们,但是不能向它们写入数据。可以通过调用缓冲 区的 asReadOnlyBuffer()方法,将任何常规缓冲区转 换为只读缓冲区,这个方法返 回一个与原缓冲区完全相同的缓冲区,并与原缓冲区共享数据,只不过它是只读的。 如果原缓冲区的内容发生了变化,只读缓冲区的内容也随之发生变化
区相当 于是现有缓冲区的一个视图窗口。调用 slice()方法可以创建一个子缓冲区。
切片范围是调用slice方法时原始缓冲区的position到limit索引之间的数据
buffer只读
只读缓冲区非常简单,可以读取它们,但是不能向它们写入数据。可以通过调用缓冲 区的 asReadOnlyBuffer()方法,将任何常规缓冲区转 换为只读缓冲区,这个方法返 回一个与原缓冲区完全相同的缓冲区,并与原缓冲区共享数据,只不过它是只读的。 如果原缓冲区的内容发生了变化,只读缓冲区的内容也随之发生变化
|