Java NIO(New IO或 Non Blocking IO)是从java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。NIO支持面向缓冲区的、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。

java IO 与 java NIO 的区别 

一、通道(Channel)与缓冲区(Buffer)

若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。简而言之,Channel 负责传输, Buffer 负责存储。

1、缓冲区(Buffer)

缓冲区(Buffer) :一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类。 
Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。

Buffer 的常用方法 

非直接缓冲区 

直接缓冲区 

/** 一、缓冲区(Buffer):在java NIO 中负者数据的存储。缓冲区就是数组。用于存储不同类型的数据。* * 根据数据类型的不同(boolean 除外),有以下 Buffer 常用子类:* ByteBuffer* CharBuffer* ShortBuffer* IntBuffer* LongBuffer* FloatBuffer* DoubleBuffer* * 上述缓冲区的管理方式几乎一致,通过allocate()获取缓冲区* * 二、缓冲区存取数据的两个核心方法:* put():存入数据到缓冲区中*       put(byte b):将给定单个字节写入缓冲区的当前位置*       put(byte[] src):将 src 中的字节写入缓冲区的当前位置*       put(int index, byte b):将指定字节写入缓冲区的索引位置(不会移动 position)* get():获取缓存区中的数据*       get() :读取单个字节*       get(byte[] dst):批量读取多个字节到 dst 中*       get(int index):读取指定索引位置的字节(不会移动 position)*       * 三、缓冲区中的四个核心属性:* capacity:容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变。* limit:界限,表示缓冲区中可以操作数据的大小。(limit后数据不能进行读写)* position:位置,表示缓冲区中正在操作数据的位置。* mark:标记,表示记录当前position位置。可以通过reset()恢复到mark的位置。* * 0<=mark<=position<=limit<=capacity* * 四、直接缓冲区与非直接缓冲区:* 非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中。*            * 直接缓冲区:通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率*          此方法返回的 缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区 。*          直接缓冲区的内容可以驻留在常规的垃圾回收堆之外.*          将直接缓冲区主要分配给那些易受基础系统的本机 I/O 操作影响的大型、持久的缓冲区。*          最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。*          直接字节缓冲区还可以过 通过FileChannel 的 map() 方法 将文件区域直接映射到内存中来创建 。该方法返回MappedByteBuffe*/
public class TestBuffer {@Testpublic void test1(){String str="abcde";//1.分配一个指定大小的缓冲区ByteBuffer buf=ByteBuffer.allocate(1024);System.out.println("--------------allocate()----------------");System.out.println(buf.position());//0System.out.println(buf.limit());//1024System.out.println(buf.capacity());//1024//2.利用put()存放数据到缓冲区中buf.put(str.getBytes());System.out.println("-------------put()-------------");System.out.println(buf.position());//5System.out.println(buf.limit());//1024System.out.println(buf.capacity());//1024//3.切换读取数据模式buf.flip();System.out.println("--------------flip()------------");System.out.println(buf.position());//0System.out.println(buf.limit());//5System.out.println(buf.capacity());//1024//4.利用get()读取缓冲区中的数据byte[] dst=new byte[buf.limit()];buf.get(dst);System.out.println(new String(dst,0,dst.length));//abcdSystem.out.println("--------------get()------------");System.out.println(buf.position());//5System.out.println(buf.limit());//5System.out.println(buf.capacity());//1024//5.rewind():可重复读buf.rewind();System.out.println("--------------rewind()------------");System.out.println(buf.position());//0System.out.println(buf.limit());//5System.out.println(buf.capacity());//1024//6.clear():清空缓冲区。但是缓冲区中的数据依然存在,但是处在“被遗忘”状态buf.clear();System.out.println("--------------clear()------------");System.out.println(buf.position());//0System.out.println(buf.limit());//1024System.out.println(buf.capacity());//1024System.out.println((char)buf.get());}@Testpublic void test2(){String str="abcde";ByteBuffer buf=ByteBuffer.allocate(1024);buf.put(str.getBytes());buf.flip();byte[] dst=new byte[buf.limit()];buf.get(dst,0,2);System.out.println(new String(dst,0,2));//abSystem.out.println(buf.position());//2//mark():标记buf.mark();buf.get(dst,2,2);//再读两个位置System.out.println(new String(dst, 2, 2));//cdSystem.out.println(buf.position());//4//reset():恢复到mark的位置buf.reset();System.out.println(buf.position());//2//判断缓冲区中是否还有剩余数据if(buf.hasRemaining()){//获取缓冲区中可以操作的数量System.out.println(buf.remaining());//3}}@Testpublic void test3(){//分配直接缓冲区ByteBuffer buf=ByteBuffer.allocate(1024);System.out.println(buf.isDirect());//false}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139

2、通道(Channel)

通道:由java.nio.channels包定义。 
Channel表示IO源与目标打开的连接。 
Channel类似于传统的“流”。但其自身不能直接访问数据,Channel只能与Buffer进行交互。

操作系统中:通道是一种通过执行通道程序管理I/O操作的控制器,它使主机(CPU和内存)与I/O操作之间达到更高的并行程度。需要进行I/O操作时,CPU只需启动通道,然后可以继续执行自身程序,通道则执行通道程序,管理与实现I/O操作。

FileChannel 的常用方法 

/** 一、通道(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 {//利用通道完成文件的复制(非直接缓冲区)@Testpublic 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}//使用直接缓冲区完成文件的复制(内存映射文件)@Testpublic 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}//通道之间的数据传输(直接缓冲区)@Testpublic 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}//分散和聚集@Testpublic 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();}}}}//输出支持的字符集@Testpublic 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());}}//字符集@Testpublic 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());//啦啦哈哈吧吧}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295

二、NIO 的非阻塞式网络通信

传统的 IO 流都是阻塞式的。也就是说,当一个线程调用 read() 或 write()时,该线程被阻塞,直到有一些数据被读取或写入,该线程在此期间不能执行其他任务。因此,在完成网络通信进行 IO 操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量客户端时,性能急剧下降。

Java NIO 是非阻塞模式的。当线程从某通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。线程通常将非阻塞 IO 的空闲时间用于在其他通道上执行 IO 操作,所以单独的线程可以管理多个输入和输出通道。因此,NIO 可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端。

选择器(Selector) 
选择器(Selector) 是 SelectableChannle 对象的多路复用器,Selector 可以同时监控多个 SelectableChannel 的 IO 状况,也就是说,利用 Selector可使一个单独的线程管理多个 Channel。Selector 是非阻塞 IO 的核心。

/** 一、使用NIO 完成网络通信的三个核心:* * 1、通道(Channel):负责连接*      java.nio.channels.Channel 接口:*           |--SelectableChannel*               |--SocketChannel*               |--ServerSocketChannel*               |--DatagramChannel*               *               |--Pipe.SinkChannel*               |--Pipe.SourceChannel*               * 2.缓冲区(Buffer):负责数据的存取* * 3.选择器(Selector):是 SelectableChannel 的多路复用器。用于监控SelectableChannel的IO状况*/
public class TestBlockingNIO {//没用Selector,阻塞型的//客户端@Testpublic void client() throws IOException{SocketChannel sChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));FileChannel inChannel=FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);ByteBuffer buf=ByteBuffer.allocate(1024);while(inChannel.read(buf)!=-1){buf.flip();sChannel.write(buf);buf.clear();}sChannel.shutdownOutput();//关闭发送通道,表明发送完毕//接收服务端的反馈int len=0;while((len=sChannel.read(buf))!=-1){buf.flip();System.out.println(new String(buf.array(),0,len));buf.clear();}inChannel.close();sChannel.close();}//服务端@Testpublic void server() throws IOException{ServerSocketChannel ssChannel=ServerSocketChannel.open();FileChannel outChannel=FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE);ssChannel.bind(new InetSocketAddress(9898));SocketChannel sChannel=ssChannel.accept();ByteBuffer buf=ByteBuffer.allocate(1024);while(sChannel.read(buf)!=-1){buf.flip();outChannel.write(buf);buf.clear();}//发送反馈给客户端buf.put("服务端接收数据成功".getBytes());buf.flip();//给为读模式sChannel.write(buf);sChannel.close();outChannel.close();ssChannel.close();}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

SelectionKey 
当调用 register(Selector sel, int ops) 将通道注册选择器时,选择器对通道的监听事件,需要通过第二个参数 ops 指定。 
可以监听的事件类型(用 可使用 SelectionKey 的四个常量 表示): 
 读 : SelectionKey.OP_READ (1) 
 写 : SelectionKey.OP_WRITE (4) 
 连接 : SelectionKey.OP_CONNECT (8) 
 接收 : SelectionKey.OP_ACCEPT (16) 
若注册时不止监听一个事件,则可以使用“位或”操作符连接。

SelectionKey:表示 SelectableChannel 和 Selector 之间的注册关系。每次向选择器注册通道时就会选择一个事件(选择键)。选择键包含两个表示为整数值的操作集。操作集的每一位都表示该键的通道所支持的一类可选择操作。

Selector 的常用方法 

public class TestNonBlockingNIO {//客户端@Testpublic void client()throws IOException{//1.获取通道SocketChannel sChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));//2.切换非阻塞模式sChannel.configureBlocking(false);//3.分配指定大小的缓冲区ByteBuffer buf=ByteBuffer.allocate(1024);//4.发送数据给服务端Scanner scan=new Scanner(System.in);while(scan.hasNext()){String str=scan.next();buf.put((new Date().toString()+"\n"+str).getBytes());buf.flip();sChannel.write(buf);buf.clear();}//5.关闭通道sChannel.close();}//服务端@Testpublic void server() throws IOException{//1.获取通道ServerSocketChannel ssChannel=ServerSocketChannel.open();//2.切换非阻塞式模式ssChannel.configureBlocking(false);//3.绑定连接ssChannel.bind(new InetSocketAddress(9898));//4.获取选择器Selector selector=Selector.open();//5.将通道注册到选择器上,并且指定“监听接收事件”ssChannel.register(selector,SelectionKey.OP_ACCEPT);//6.轮询式的获取选择器上已经“准备就绪”的事件while(selector.select()>0){//7.获取当前选择器中所有注册的“选择键(已就绪的监听事件)”Iterator<SelectionKey> it=selector.selectedKeys().iterator();while(it.hasNext()){//8.获取准备“就绪”的事件SelectionKey sk=it.next();//9.判断具体是什么时间准备就绪if(sk.isAcceptable()){//10.若“接收就绪”,获取客户端连接SocketChannel sChannel=ssChannel.accept();//11.切换非阻塞模式sChannel.configureBlocking(false);//12.将该通道注册到选择器上sChannel.register(selector, SelectionKey.OP_READ);}else if(sk.isReadable()){//13.获取当前选择器上“读就绪”状态的通道SocketChannel sChannel=(SocketChannel)sk.channel();//14.读取数据ByteBuffer buf=ByteBuffer.allocate(1024);int len=0;while((len=sChannel.read(buf))>0){buf.flip();System.out.println(new String(buf.array(),0,len));buf.clear();}}//15.取消选择键SelectionKeyit.remove();}}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

DatagramChannel 
Java NIO中的DatagramChannel是一个能收发UDP包的通道。

public class TestNonBlockNIO2 {@Testpublic void send() throws IOException{DatagramChannel dc=DatagramChannel.open();dc.configureBlocking(false);ByteBuffer buf=ByteBuffer.allocate(1024);Scanner scan=new Scanner(System.in);while(scan.hasNext()){String str=scan.next();buf.put((new Date().toString()+"\n"+str).getBytes());buf.flip();dc.send(buf, new InetSocketAddress("127.0.0.1", 9898));buf.clear();}dc.close();}@Testpublic void receive() throws IOException{DatagramChannel dc=DatagramChannel.open();dc.configureBlocking(false);dc.bind(new InetSocketAddress(9898));Selector selector=Selector.open();dc.register(selector, SelectionKey.OP_READ);while(selector.select()>0){Iterator<SelectionKey> it=selector.selectedKeys().iterator();while(it.hasNext()){SelectionKey sk=it.next();if(sk.isReadable()){ByteBuffer buf=ByteBuffer.allocate(1024);dc.receive(buf)
;buf.flip();System.out.println(new String(buf.array(),0,buf.limit()));buf.clear();}}it.remove();}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

管道 (Pipe) 
Java NIO 管道是2个线程之间的单向数据连接。Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取。

public class TestPipe {@Testpublic void test1()throws IOException{//1.获取管道Pipe pipe=Pipe.open();//2.将缓冲区中的数据写入管道ByteBuffer buf=ByteBuffer.allocate(1024);Pipe.SinkChannel sinkChannel=pipe.sink();buf.put("通过单向管道发送数据".getBytes());buf.flip();sinkChannel.write(buf);//3.读取缓冲区中的数据Pipe.SourceChannel sourceChannel=pipe.source();buf.flip();int len=sourceChannel.read(buf);System.out.println(new String(buf.array(),0,len));sourceChannel.close();sinkChannel.close();}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

三、NIO.2 – Path 、Paths 、Files

Path 与 Paths

  • java.nio.file.Path 接口代表一个平台无关的平台路径,描述了目录结构中文件的位置。
  • Paths 提供的 get() 方法用来获取 Path 对象:Path get(String first, String … more) : 用于将多个字符串串连成路径。
  • Path 常用方法: 
    • boolean endsWith(String path) : 判断是否以 path 路径结束
    • boolean startsWith(String path) : 判断是否以 path 路径开始
    • boolean isAbsolute() : 判断是否是绝对路径
    • Path getFileName() : 返回与调用 Path 对象关联的文件名
    • Path getName(int idx) : 返回的指定索引位置 idx 的路径名称
    • int getNameCount() : 返回Path 根目录后面元素的数量
    • Path getParent() :返回Path对象包含整个路径,不包含Path 对象指定的文件路径
    • Path getRoot() :返回调用 Path 对象的根路径
    • Path resolve(Path p) :将相对路径解析为绝对路径
    • Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象
    • String toString() : 返回调用 Path 对象的字符串表示形式

Files 类 
java.nio.file.Files 用于操作文件或目录的工具类。

  • Files常用方法:

    • Path copy(Path src, Path dest, CopyOption … how) : 文件的复制
    • Path createDirectory(Path path, FileAttribute< ? > … attr) : 创建一个目录
    • Path createFile(Path path, FileAttribute< ? > … arr) : 创建一个文件
    • void delete(Path path) : 删除一个文件
    • Path move(Path src, Path dest, CopyOption…how) : 将 src 移动到 dest 位置
    • long size(Path path) : 返回 path 指定文件的大小
  • Files常用方法:用于判断

    • boolean exists(Path path, LinkOption … opts) : 判断文件是否存在
    • boolean isDirectory(Path path, LinkOption … opts) : 判断是否是目录
    • boolean isExecutable(Path path) : 判断是否是可执行文件
    • boolean isHidden(Path path) : 判断是否是隐藏文件
    • boolean isReadable(Path path) : 判断文件是否可读
    • boolean isWritable(Path path) : 判断文件是否可写
    • boolean notExists(Path path, LinkOption … opts) : 判断文件是否不存在
    • public static < A extends BasicFileAttributes> A readAttributes(Path path,Class< A > type,LinkOption…options) : 获取与 path 指定的文件相关联的属性。
  • Files常用方法:用于操作内容

    • SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连接,how 指定打开方式。
    • DirectoryStream newDirectoryStream(Path path) : 打开 path 指定的目录
    • InputStream newInputStream(Path path, OpenOption…how):获取 InputStream 对象
    • OutputStream newOutputStream(Path path, OpenOption…how) : 获取 OutputStream 对象

尚硅谷java——NIO(New IO)相关推荐

  1. 尚硅谷Java入门视频教程导读及第一章

    尚硅谷Java入门视频教程导读及第一章 JAVA基础学习导读-编程入门 0.1概述 0.2 计算机硬件介绍 中央处理器(CPU) 存储设备 内存 比特(bit)和字节(byte) 内存 输入和输出设备 ...

  2. 尚硅谷Java、HTML5前端、全栈式开发视频

    Java基础阶段: 一.20天横扫Java基础(课堂实录) https://pan.baidu.com/s/1htTzZRQ 二.尚硅谷Java基础实战--Bank项目 http://pan.baid ...

  3. 尚硅谷Java数据结构和java算法,韩顺平数据结构和算法课后作业01

    尚硅谷Java数据结构和java算法,韩顺平数据结构和算法课后作业第一题 要求: 1)在前面的基础上,将稀疏数组保存到磁盘上,比如map.data 2) 恢复原来的数组时,读取map.data进行恢复 ...

  4. 尚硅谷Java入门视频教程第十七章——Java9Java10Java11新特性

    尚硅谷Java入门视频教程第十七章--Java9&Java10&Java11新特性 第17章:Java9&Java10&Java11新特性 17.1 Java 9 的新 ...

  5. 史上讲的最好的Java NIO与IO的区别与应用

    如果下面的内容看的不太懂,直接看最后的摘要和总结. 在研究Java NIO和IO API时,很快就会发现一个问题: 我什么时候应该使用IO,什么时候应该使用NIO? 在本文中,我将尝试阐明Java N ...

  6. Java NIO与IO的区别和比较

    Java NIO与IO的区别和比较 导读 J2SE1.4以上版本中发布了全新的I/O类库.本文将通过一些实例来简单介绍NIO库提供的一些新特性:非阻塞I/O,字符转换,缓冲以及通道. 一. 介绍NIO ...

  7. Java NIO系列教程(十二) Java NIO与IO

    原文地址:http://tutorials.jenkov.com/java-nio/nio-vs-io.html 作者:Jakob Jenkov   译者:郭蕾    校对:方腾飞 当学习了Java ...

  8. Java NIO和IO的主要区别

    2019独角兽企业重金招聘Python工程师标准>>> Java NIO和IO的主要区别如下: 1.NIO 的创建目的是为了让 Java 程序员可以实现高速 I/O 而无需编写自定义 ...

  9. 尚硅谷Java入门视频教程(在线答疑+Java面试真题)

    Java核心技术 一.Java基本语法 1.关键字与保留字 关键字的定义和特点 被Java语言赋予了特殊含义,用做专门用途的字符串(单词) 关键字中所有字母都为小写 保留字的定义 现有Java版本尚未 ...

最新文章

  1. 微信jssdk开发,PHP,必要步骤
  2. Codeforces Round #720 (Div. 2) C. Nastia and a Hidden Permutation 交互
  3. SSIS添加分区-动态
  4. java 一些容易忽视的小点-类和对象
  5. python入门——列表+元组+字典+集合
  6. 我的世界java出生蘑菇岛,《我的世界》:粉丝强推,出生超巨型蘑菇岛和村庄连在一起...
  7. 英文对“ Big O”符号的解释是什么?
  8. Table is marked as crashed and should be repaire 解决方法
  9. 牛客寒假训练营1 K 冒险公社(线性dp)
  10. JS获取手机型号和系统版本
  11. 2021-09-25 WPF上位机 29-3D绘图的对象,变形,鼠标操控,鼠标事件,2D在3D中展示
  12. 中文搜索引擎2010Q2市场份额
  13. python取省边界_提取行政区边界经纬度坐标(高德+百度)
  14. 给一个大表增加一个字段,带默认值
  15. 【无标题】c++日常练习(16)——从中序与前序遍历序列构造二叉树
  16. 西部世界科普时间:FIL将从4月15日开始减产? 谣言!
  17. ScrollView水平滑动条选中条目的居中显示
  18. 西门子1200PLC和KTP700触摸屏控制西门子V90伺服电机例子程序
  19. 在vue项目中使用html2canvas截图(固定区域截图)
  20. cassandra install troubleshooting

热门文章

  1. 关于MBR与 EFI的讨论!
  2. eclipse导入android项目卡死,水晶虎宫殿34998 -官网
  3. Python数据可视化:2018年电影分析
  4. C/C++发展历程和标准
  5. 【iOS开发】导航栏,类似淘宝“我的淘宝”导航栏
  6. 和信创天云桌面系统VENGD文件上传漏洞复现
  7. 手把手教会你:VMware Esxi系统安装步骤(版本7.0.3)
  8. 余姚小学2016Pascal全题解答
  9. MySQL sql常用语句
  10. selenium—元素、浏览器、元素信息操作常用API