高并发之NIO、 AIO、BIO

本文介绍了Java NIO(New I/O)的概念及其与传统IO的区别,包括面向缓冲而非面向流、同步非阻塞IO等特点。通过代码示例展示了如何利用NIO进行文件复制、内存映射文件读写等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 什么是NIO

NIO是New I/O的简称,与旧式的基于流的I/O方法相对,从名字看,它表示新的一套Java I/O标 准。它是在Java 1.4中被纳入到JDK中的,并具有以下特性: 

  • NIO是基于块(Block)的,它以块为基本单位处理数据 (硬盘上存储的单位也是按Block来存储,这样性能上比基于流的方式要好一些)
  • 为所有的原始类型提供(Buffer)缓存支持 
  • 增加通道(Channel)对象,作为新的原始 I/O 抽象
  • 支持锁(我们在平时使用时经常能看到会出现一些.lock的文件,这说明有线程正在使用这把锁,当线程释放锁时,会把这个文件删除掉,这样其他线程才能继续拿到这把锁)和内存映射文件的文件访问接口 
  • 提供了基于Selector的异步网络I/O 

Java NIO为什么是同步非阻塞的?

java中,同步就是说多线程去访问某个方法的时候,当访问到某些共享数据是同一时刻只能有一个线程去访问
阻塞就是说当前线程被挂起,例如调用了Thread.sleep() ;
Java NIO和IO的主要区别
下表总结了Java NIO和IO之间的主要差别,我会更详细地描述表中每部分的差异。
IO               	NIO
面向流             面向缓冲(块)
同步阻塞IO      同步 非阻塞IO
无                    选择器

面向流与面向缓冲
Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

阻塞与非阻塞IO
Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

package entryNIO;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class BufferAndChannel {

	public static void main(String[] args)  {
		String string="C:\\Users\\hp\\Desktop";
		long startTime=System.currentTimeMillis();
		nioCopyFile(string+"\\FileChannelImpl.java",string+"\\1.java");
		System.out.println("用时:"+(System.currentTimeMillis()-startTime)+"毫秒");
		System.out.println("读取完毕");
	}

	public static void nioCopyFile(String resource, String destination) {
		ByteBuffer buffer = null;//字节缓冲区
		try(//JDK 1.7 新特性  try-with-resource ,凡是实现了Closeable接口就可以用这种写法,而无需关闭流
				FileInputStream fis = new FileInputStream(resource);
				FileOutputStream fos = new FileOutputStream(destination);
			// Used by FileInputStream.getChannel() and RandomAccessFile.getChannel()
				FileChannel readChannel = fis.getChannel(); // 读文件通道
				FileChannel writeChannel = fos.getChannel(); // 写文件通道
		){
				buffer=ByteBuffer.allocate(1024*1024); // 为字节缓冲区分配空间	
				
				int len=0;
				while ((len = readChannel.read(buffer))!=-1) {
					/*
					      反转此缓冲区。首先将限制设置为当前位置,然后将位置设置为 0。如果已定义了标记,则丢弃该标记。 
					      在一系列通道读取或放置 操作之后,调用此方法为一系列通道写入或相对获取 操作做好准备。例如: 
					      当将数据从一个地方传输到另一个地方时,经常将此方法与 compact 方法一起使用。
					 */
					buffer.flip();//反转此缓冲区
					writeChannel.write(buffer); // 写入文件
					buffer.clear();//清空缓冲区
				}
				
				
		} catch (Exception e) {
			e.printStackTrace();
        } 
   }
		
}


//理论上会比使用BufferedReader等快一点


package entryNIO;
import java.nio.ByteBuffer;
public class BufferAndChannel {

	public static void main(String[] args)  {
		
		long startTime=System.currentTimeMillis();
		try {
			Ha.main(args);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("用时:"+(System.currentTimeMillis()-startTime)+"毫秒");
		System.out.println("读取完毕");
	}	
}


class Ha{
	public static void main(String[] args) throws Exception {
		ByteBuffer b = ByteBuffer.allocate(15); // 15个字节大小的缓冲区
		sop(b);//打印
		for (int i = 0; i < 10; i++) {
			// 存入10个字节数据
			b.put((byte) i);
		}
		sop(b);
		/*
		        该操作会重置position,通常,将buffer从写模式转换为读 模式时需要执行 flip()方法
		         该方法不仅重置了当前的position为0,还将limit设置到当前position的位置 。
		   limit的意义在于,来确定哪些数据是有意义的,换句话说,从position到limit之间的数据才是有意义的数据,
		        因为是上次操作的数据。所以flip操作往往是读写转换的意思。
		 */
		b.flip(); // 重置position,一般会重置为0
		sop(b);
		for (int i = 0; i < 5; i++) {
			System.out.print(b.get());
		}
		System.out.println();
		sop(b);
		b.flip();
		sop(b);

	}

	private static void sop(ByteBuffer b) {
    //这里要区别下容量和上限,比如一个Buffer有15KB,那么15KB就是容量,我将5KB的文件读到Buffer中,那么上限就是5KB。
		System.out.println("limit=" + b.limit() + " capacity=" + b.capacity()
				+ " position=" + b.position());
	}
}



将文件映射到内存

package entryNIO;

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class BufferAndChannel {

	public static void main(String[] args) {

		long startTime = System.currentTimeMillis();
		try (RandomAccessFile raf = new RandomAccessFile("C:\\FileChannelImpl.java", "rw"); 
			 FileChannel fc = raf.getChannel();) {
			// 将文件映射到内存中
			MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, raf.length());
			//byte[] byteArray=new byte[1024*1024];
			while (mbb.hasRemaining()) {// between the current position and the limit.在这范围内是否还有
				System.out.print((char) mbb.get());//mbb.get(dst, offset, length)
			}
			mbb.put(0, (byte) 98); // 修改文件
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("用时:" + (System.currentTimeMillis() - startTime) + "毫秒");
		System.out.println("读取完毕");
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值