簡介
- NIO與原來的IO有同樣的作用和目的带膜,但是使用方式完全不同玖雁,NIO支持面向緩存區(qū)的、基于通道的IO操作翰铡。NIO將以更加高效的方式進行文件的讀寫操作。
java NIO 與 IO的主要區(qū)別
IO | NIO |
---|---|
面向流 | 面向緩存區(qū) |
阻塞IO(Blocking IO) | 非阻塞IO(Non Blocking IO) |
(無) | 選擇器 |
- Channel 負責(zé)傳輸
- Buffer 負責(zé)存儲
緩存區(qū)存取數(shù)據(jù)的核心方法
put() 寫
get() 讀
flip() 切換模式
緩存區(qū)的四個核心屬性
private int mark = -1; //標記唱捣,表示當前position的位置两蟀。可以通過reset()恢復(fù)到mark的位置
private int position = 0; //位置震缭,表示緩存區(qū)正在操作數(shù)據(jù)的位置赂毯。
private int limit; //界限,表示緩存區(qū)可以操作數(shù)據(jù)的大小拣宰。(limit后數(shù)據(jù)不能進行讀寫)
private int capacity; //容量党涕,表示緩存區(qū)的最大存儲數(shù)據(jù)的容量。一旦聲明不能改變巡社。
0 <= mark <= position <= limit <= capacity
public class test1 {
public static void main(String[] args) {
test2();
}
public static void test2(){
String str = "abcde";
//1. 分配一個指定大小的緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024);
System.out.println("----------allocate()---------");
System.out.println(buf.position());//0
System.out.println(buf.limit());//1024
System.out.println(buf.capacity());//1024
//2. 利用put()存入數(shù)據(jù)到緩沖區(qū)中
buf.put(str.getBytes());
System.out.println("----------put()---------");
System.out.println(buf.position());//5
System.out.println(buf.limit());//1024
System.out.println(buf.capacity());//1024
//3.切換讀取數(shù)據(jù)模式
buf.flip();
System.out.println("----------put()---------");
System.out.println(buf.position());//0
System.out.println(buf.limit());//5
System.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));
System.out.println("----------get()---------");
System.out.println(buf.position());//5
System.out.println(buf.limit());//5
System.out.println(buf.capacity());//1024
//5.可重復(fù)讀
buf.rewind();
System.out.println("----------rewind()---------");
System.out.println(buf.position());//0
System.out.println(buf.limit());//5
System.out.println(buf.capacity());//1024
//6.clear():清空緩沖區(qū),但是緩沖區(qū)中的數(shù)據(jù)依然存在膛堤,但是出于“被遺忘”狀態(tài)
buf.clear();
System.out.println("----------clear()---------");
System.out.println(buf.position());//0
System.out.println(buf.limit());//1024
System.out.println(buf.capacity());//1024
//mark:標記,表示記錄當前position的位置晌该》世螅可以通過reset()恢復(fù)到mark位置
}
}
直接緩沖區(qū)和非直接緩沖區(qū)
非直接緩存區(qū):allocate()方法分配緩存區(qū),將緩存區(qū)建立在JVM的內(nèi)存中(堆朝群,數(shù)組)
直接緩存區(qū):allocateDirect()方法分配直接緩存區(qū)燕耿,將緩存區(qū)建立在物理內(nèi)存中,可以提高效率
-
非直接緩沖區(qū)
-
直接緩沖區(qū)
Channel
- Channel表示IO源與目標打開的連接姜胖。Channel類似于傳統(tǒng)的“流”誉帅。只不過Channel本身不能直接訪問數(shù)據(jù),Channel只能與Buffer進行交互右莱。
- 分類:
FileChannel
-
SelectableChannel
- SocketChannel
- SockeServerChannel
- DatagramChannel
- Pipe.SinkChannel
- Pipe.SourceChannel
- 獲取通道
- Java針對支持通道的類提供了getChannel()方法:
- 本地IO:
- FileInputStream/FileOutputStream
- RamdomAccessFile
- 網(wǎng)絡(luò)IO:
- Socket
- ServerSocket
- DatagramSocket
- 在JDK 1.7中的NIO.2 針對各個通道提供了靜態(tài)方法open()
- 在JDK 1.7中的NIO.2 的Files工具類的newByteChannel()
- 利用通道完成文件的復(fù)制(非直接緩沖區(qū))
public class FileCopy {
public static void main(String[] args) {
test();
}
public static void test(){
FileInputStream fis = null;
FileOutputStream fos = null;
//獲取通道
FileChannel inchannel = null;
FileChannel outChannel = null;
try {
fis = new FileInputStream("D:/1.jpg");
fos = new FileOutputStream("D:/2.jpg");
inchannel = fis.getChannel();
outChannel = fos.getChannel();
//分配指定大小的緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024);
//將通道中的數(shù)據(jù)存入到緩沖區(qū)中
while (inchannel.read(buf) != -1){
buf.flip(); //切換到讀數(shù)據(jù)的模式
//將緩沖區(qū)的數(shù)據(jù)寫入到通道中
outChannel.write(buf);
buf.clear();//清空緩存區(qū)
}
}catch (IOException e){
e.printStackTrace();
}finally{
if(inchannel != null){
try {
inchannel.close();
}catch (IOException e){
e.printStackTrace();
}
}
if(outChannel != null){
try {
outChannel.close();
}catch (IOException e){
e.printStackTrace();
}
}
if(fis != null){
try {
fis.close();
}catch (IOException e){
e.printStackTrace();
}
}
if(fos != null){
try {
fos.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
- 使用直接緩沖區(qū)完成文件的復(fù)制(內(nèi)存映射文件)
public class FileCopy2 {
public static void main(String[] args) {
test3();
}
private static void test3() {
try {
FileChannel inChannel = FileChannel.open(Paths.get("D:/1.jpg"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("D:/3.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ,
StandardOpenOption.CREATE_NEW);
//內(nèi)存映射文件
MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
//直接對緩沖區(qū)進行數(shù)據(jù)的讀寫操作
byte[] dst = new byte[inMappedBuf.limit()];
inMappedBuf.get(dst);
outMappedBuf.put(dst);
} catch (IOException e) {
e.printStackTrace();
}
}
}
分散(Scatter)和聚集(Gather)
- 分散讀妊料恰(Scattering Reads): 將通道中的數(shù)據(jù)分散到多個緩沖區(qū)中
- 聚集寫入(Gathering Writes): 將多個緩沖區(qū)中的數(shù)據(jù)聚集到通道中
public class FileCopy3 {
public static void main(String[] args) {
test4();
}
private static void test4() {
try {
RandomAccessFile raf1 = new RandomAccessFile("D:/3.txt","rw");
//1.獲取通道
FileChannel channel1 = raf1.getChannel();
//2.分配指定大小的緩沖區(qū)
ByteBuffer buf1 = ByteBuffer.allocate(1);
ByteBuffer buf2 = ByteBuffer.allocate(1024);
//3.分散讀取
ByteBuffer[] bufs = {buf1,buf2};
channel1.read(bufs);
for (ByteBuffer byteBuffer : bufs) {
byteBuffer.flip();
}
//聚集寫入
RandomAccessFile raf2 = new RandomAccessFile("D:/7.txt","rw");
FileChannel channel2 = raf2.getChannel();
channel2.write(bufs);
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
}
Selector
- 是SelectableChannel的多路復(fù)用器,用于監(jiān)控SelectableChannel的狀態(tài).它是實現(xiàn)NIO非阻塞的關(guān)鍵慢蜓。
- fileChannel不能切換成非阻塞式模式亚再。
阻塞式網(wǎng)絡(luò)IO實現(xiàn)
- 客戶端
public class FileTest {
public static void main(String[] args) {
client();
}
//客戶端
private static void client() {
try {
//1.獲取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("9.236.37.131", 9898));
FileChannel inChannel = FileChannel.open(Paths.get("D:/1.jpg"), StandardOpenOption.READ);
//2.分配指定大小的緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024);
//3.讀取本地文件,并發(fā)送到服務(wù)端
while (inChannel.read(buf) != -1){
buf.flip();
sChannel.write(buf);
buf.clear();
}
//單向關(guān)閉自己的輸出流晨抡,并未關(guān)閉連接针余,不寫這個服務(wù)器端并不知道傳輸?shù)膱D片結(jié)束了饲鄙。一直卡在ssChannel.accept()
sChannel.shutdownOutput();
//接收服務(wù)器的反饋
int len = 0;
while ((len = sChannel.read(buf))!=-1){
buf.flip();
System.out.println(new String(buf.array(),0,len));
buf.clear();
}
//4.關(guān)閉通道
inChannel.close();
sChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 服務(wù)端
public class FileTest2 {
public static void main(String[] args) {
server();
}
//服務(wù)端
private static void server() {
try {
//1.獲取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
FileChannel outChannel = FileChannel.open(Paths.get("D:/10.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//2.綁定鏈接
ssChannel.bind(new InetSocketAddress(9898));
//3.獲取客戶端連接的通道
SocketChannel sChannel = ssChannel.accept();
//4.分配指定大小的緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024);
//5.接收客戶端的數(shù)據(jù)凄诞,并保存到本地
while (sChannel.read(buf)!= -1){
buf.flip();
outChannel.write(buf);
buf.clear();
}
//發(fā)送反饋給客戶端
buf.put("服務(wù)器接收數(shù)據(jù)成功".getBytes());
buf.flip();
sChannel.write(buf);
//6.關(guān)閉通道
sChannel.close();
outChannel.close();
ssChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
非阻塞式網(wǎng)絡(luò)IO實現(xiàn)
- 客戶端
public class NonBlockClient {
public static void main(String[] args) {
client();
}
private static void client() {
try {
//1.獲取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("9.236.37.131", 9898));
//2.切換非阻塞模式
sChannel.configureBlocking(false);
//3.分配指定大小的緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024);
//4.發(fā)送數(shù)據(jù)給服務(wù)器
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.關(guān)閉通道
sChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 服務(wù)端
public class NonBlockServer {
public static void main(String[] args) {
server();
}
private static void server() {
try {
//1.獲取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
//2.切換非阻塞模式
ssChannel.configureBlocking(false);
//3.綁定鏈接
ssChannel.bind(new InetSocketAddress(9898));
//4.獲取選擇器
Selector selector = Selector.open();
//5.將通道注冊到選擇器上圆雁,并且指定“監(jiān)聽接收事件”
//SelectionKey-----OP_ACCEPT,OP_CONNECT,OP_READ,OP_WRITE
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
//6.輪詢式的獲取選擇器上已經(jīng)“準備就緒”的事件
while (selector.select()>0){
//7.獲取當前選擇器中所有注冊的“選擇鍵(已就緒的監(jiān)聽事件)”
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. 獲取當前選擇器上“讀就緒”狀態(tài)的通道
SocketChannel sChannel = (SocketChannel)sk.channel();
//14. 讀取數(shù)據(jù)
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.取消選擇鍵SelectionKey
it.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- DatagramChannel --UDP用法類似
-
Pipe