Selector会不断地轮询注册在其上的Channel,如果某个Channel上面有新的TCP连接接入、读和写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的Set集合,进行后续的I/O操作。
Java NIO (New IO Non Blocking IO)是从Java1.4版本开始引入的一个新的IO API ,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。
IMG_2197(20201121-101659).JPG IMG_2198(20201121-103358).JPG IMG_2199(20201121-103413).JPG IMG_2200(20201121-103533).JPG1、缓冲区(Buffer)
缓冲区(Buffer) :一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类。
Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。
/**
* @author qiz
*/
public class NIO {
public static void main(String[] args) {
//分配一个指定大小的缓冲区
//缓冲区的管理方式几乎一致,通过allocate()获取缓冲区
ByteBuffer allocate = ByteBuffer.allocate(1024);
//缓冲区存区数据的两大核心方法
//put() 存入数据 get()获取缓冲区数据
/**
* private int mark = -1;
* private int position = 0; 位置,表示缓冲区中正在操作数据的位置
* private int limit; 限制/界限 缓冲区中可以操作数据的大小 limit后面的数据不能改变
* private int capacity;容量,表示缓冲区最大存储数据的容量,一旦声明不能改变。
*
*/
System.out.println(allocate.limit());
System.out.println(allocate.position());
System.out.println(allocate.capacity());
}
}
Buffer 的常用方法
非直接缓冲区
这里写图片描述
直接缓冲区
DMA(Direct Memory Access,直接存储器访问)。在DMA出现之前,CPU与外设之间的数据传送方式有程序传送方式、中断传送方式。CPU是通过系统总线与其他部件连接并进行数据传输。
image
DMA的出现就是为了解决批量数据的输入/输出问题。DMA是指外部设备不通过CPU而直接与系统内存交换数据的接口技术。这样数据的传送速度就取决于存储器和外设的工作速度。
在DMA方式中,数据的传送方向、存放数据的内存始址以及传送的数据块长度等都由CPU控制,而在通道方式中,这些都由通道来进行控制。另外,DMA方式每台设备至少需要一个DMA控制器,一个通道控制器可以控制多台设备。
DMA是为了解放在IO操作时每一个字符都需要由CPU来管理传输, 在接到网卡中断时, cpu会初始化DMA, 打个比方, 你在打游戏,
可是门铃响了, 外卖到了, 你new 了一个girl friend 告诉她, 你定的什么外卖, 数量多少, 需要放在那里, 然后你继续好好打游戏, 其他交由她去完成(去的快递, 放入指定位置, 校验是否完成), done之后, 会回来中断你打游戏, 你就可以去吃外卖了, 节省你的精力
明白了DMA就好理解NIO的Channel了吧.
再来张图:
通道(Channel)用于源节点与目标节点的连接。在Java NIO中负责缓冲区数据的传输。Channel本身不存储数据,因此需要配合缓冲区进行传输。
image.png/*
* 一、通道(Channel):用于源节点与目标节点的连接。在java NIO中负责缓冲区中数据的传输。Channel本身不存储数据,需要配合缓冲区进行传输。
*
* 二、通道的主要实现类
* java.nio.channels.Channel 接口:
* |--FileChannel:用于读取、写入、映射和操作文件的通道。
* |--SocketChannel:通过 TCP 读写网络中的数据。
* |--ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
* |--DatagramChannel:通过 UDP 读写网络中的数据通道。
*
* 三、获取通道
* 1.java针对支持通道的类提供了getChannel()方法
* 本地IO:
* FileInputStream/FileOutputStream
* RandomAccessFile
*
* 网络IO:
* Socket
* ServerSocket
* DatagramSocket
*
* 2.在JDK 1.7 中的NIO.2 针对各个通道提供了静态方法 open()
* 3.在JDK 1.7 中的NIO.2 的Files工具类的newByteChannel()
*
* 四、通道之间的数据传输
* transferFrom()
* transferTo()
*
* 五、分散(Scatter)与聚集(Gather)
* 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
* 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
*
* 六、字符集:Charset
* 编码:字符串-》字符数组
* 解码:字符数组-》字符串
*/
public class TestChannel {
//利用通道完成文件的复制(非直接缓冲区)
@Test
public void test1(){
long start=System.currentTimeMillis();
FileInputStream fis=null;
FileOutputStream fos=null;
FileChannel inChannel=null;
FileChannel outChannel=null;
try{
fis=new FileInputStream("d:/1.avi");
fos=new FileOutputStream("d:/2.avi");
//1.获取通道
inChannel=fis.getChannel();
outChannel=fos.getChannel();
//2.分配指定大小的缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024);
//3.将通道中的数据存入缓冲区中
while(inChannel.read(buf)!=-1){
buf.flip();//切换读取数据的模式
//4.将缓冲区中的数据写入通道中
outChannel.write(buf);
buf.clear();//清空缓冲区
}
}catch(IOException e){
e.printStackTrace();
}finally{
if(outChannel!=null){
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inChannel!=null){
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
long end=System.currentTimeMillis();
System.out.println("耗费时间:"+(end-start));//耗费时间:1094
}
//使用直接缓冲区完成文件的复制(内存映射文件)
@Test
public void test2() {
long start=System.currentTimeMillis();
FileChannel inChannel=null;
FileChannel outChannel=null;
try {
inChannel = FileChannel.open(Paths.get("d:/1.avi"), StandardOpenOption.READ);
outChannel=FileChannel.open(Paths.get("d:/2.avi"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
//内存映射文件
MappedByteBuffer inMappedBuf=inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMappedBuf=outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
//直接对缓冲区进行数据的读写操作
byte[] dst=new byte[inMappedBuf.limit()];
inMappedBuf.get(dst);
outMappedBuf.put(dst);
} catch (IOException e) {
e.printStackTrace();
}finally{
if(outChannel!=null){
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inChannel!=null){
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
long end=System.currentTimeMillis();
System.out.println("耗费的时间为:"+(end-start));//耗费的时间为:200
}
//通道之间的数据传输(直接缓冲区)
@Test
public void test3(){
long start=System.currentTimeMillis();
FileChannel inChannel=null;
FileChannel outChannel=null;
try {
inChannel = FileChannel.open(Paths.get("d:/1.avi"), StandardOpenOption.READ);
outChannel=FileChannel.open(Paths.get("d:/2.avi"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
inChannel.transferTo(0, inChannel.size(), outChannel);
outChannel.transferFrom(inChannel, 0, inChannel.size());
} catch (IOException e) {
e.printStackTrace();
}finally{
if(outChannel!=null){
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inChannel!=null){
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
long end=System.currentTimeMillis();
System.out.println("耗费的时间为:"+(end-start));//耗费的时间为:147
}
image.png
//分散和聚集
@Test
public void test4(){
RandomAccessFile raf1=null;
FileChannel channel1=null;
RandomAccessFile raf2=null;
FileChannel channel2=null;
try {
raf1=new RandomAccessFile("1.txt","rw");
//1.获取通道
channel1=raf1.getChannel();
//2.分配指定大小的缓冲区
ByteBuffer buf1=ByteBuffer.allocate(100);
ByteBuffer buf2=ByteBuffer.allocate(1024);
//3.分散读取
ByteBuffer[] bufs={buf1,buf2};
channel1.read(bufs);
for(ByteBuffer byteBuffer : bufs){
byteBuffer.flip();
}
System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
System.out.println("--------------------");
System.out.println(new String(bufs[1].array(),0,bufs[1].limit()));
//4.聚集写入
raf2=new RandomAccessFile("2.txt", "rw");
channel2=raf2.getChannel();
channel2.write(bufs);
}catch (IOException e) {
e.printStackTrace();
}finally{
if(channel2!=null){
try {
channel2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(channel1!=null){
try {
channel1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(raf2!=null){
try {
raf2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(raf1!=null){
try {
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//输出支持的字符集
@Test
public void test5(){
Map<String,Charset> map=Charset.availableCharsets();
Set<Entry<String,Charset>> set=map.entrySet();
for(Entry<String,Charset> entry:set){
System.out.println(entry.getKey()+"="+entry.getValue());
}
}
//字符集
@Test
public void test6(){
Charset cs1=Charset.forName("GBK");
//获取编码器
CharsetEncoder ce=cs1.newEncoder();
//获取解码器
CharsetDecoder cd=cs1.newDecoder();
CharBuffer cBuf=CharBuffer.allocate(1024);
cBuf.put("啦啦哈哈吧吧");
cBuf.flip();
//编码
ByteBuffer bBuf=null;
try {
bBuf = ce.encode(cBuf);
} catch (CharacterCodingException e) {
e.printStackTrace();
}
for(int i=0;i<12;i++){
System.out.println(bBuf.get());//-64-78-64-78-71-2-7-2-80-55-80-55
}
//解码
bBuf.flip();
CharBuffer cBuf2=null;
try {
cBuf2 = cd.decode(bBuf);
} catch (CharacterCodingException e) {
e.printStackTrace();
}
System.out.println(cBuf2.toString());//啦啦哈哈吧吧
}
}
网友评论