日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

一、什么是NIO

1.概念

NIO是JAVA1.4中引入的,被稱為new I/O,也有說是non-blocking I/O,NIO被成為同步非阻塞的IO。

 

2.跟BIO流的區別

  1. BIO是面向流的,NIO是面向塊(緩沖區)的
  2. BIO的流都是同步阻塞的,而NIO同步非阻塞的
  3. NIO會等待數據全部傳輸過來再讓線程處理,BIO是直接讓線程等待。
  4. NIO有選擇器,而BIO沒有。
  5. NIO是采用管道和緩存區的形式來處理數據的,而BIO是采用輸入輸出流來處理的。
  6. NIO是可以雙向的,BIO只能夠單向。

 

二、NIO常用組件Channel和Buffer的使用

1.代碼

這里以文件復制為例

public class test {
    public static void main(String[] args){
        try{
            //存在的照片
            File inFile=new File("C:\Users\Administrator\Desktop\study.PNG");
            //復制后要存放照片的地址
            File outFile=new File("C:\Users\Administrator\Desktop\study1.PNG");
            //打開流
            FileInputStream fileInputStream=new FileInputStream(inFile);
            FileOutputStream fileOutputStream=new FileOutputStream(outFile);
            /**
             * RandomaccessFile accessFile=new RandomAccessFile(inFile,"wr");
             *  FileChannel inFileChannel=accessFile.getChannel();
             *  和下面兩行代碼是一樣的,都是可以拿到FileChannel
             */
            //獲取Channel
            FileChannel inFileChannel=fileInputStream.getChannel();
            FileChannel outFileChannel=fileOutputStream.getChannel();
		   //創建buffer
            ByteBuffer buffer=ByteBuffer.allocate(1024*1024);
			//讀取到buffer中
            while (inFileChannel.read(buffer)!=-1){
                //翻轉一下,就可以讀取到全部數據了
                buffer.flip();
                outFileChannel.write(buffer);
                //讀取完后要clear
                buffer.clear();
            }
            //關閉
            inFileChannel.close();
            outFileChannel.close();
            fileInputStream.close();
            fileOutputStream.close();
        }catch (Exception e){}

    }
}
復制代碼

我的桌面上的確多了一張一模一樣的圖片

2.解釋

使用NIO的話,需要注意幾個步驟:

  1. 打開流
  2. 獲取通道
  3. 創建Buffer
  4. 切換到讀模式 buffer.flip()
  5. 切換到寫模式 buffer.clear(); 其實這里也看不出來它是怎么使用緩沖區的,上面這段代碼中的while循環的作用和下面的代碼是一樣的
 while ((i=fileInputStream.read())!=-1){
                fileOutputStream.write(i);
          }

讓我們趕緊開始NIO的編程

三、BIO和NIO的區別

學習了Channel和Buffer的使用,我們就可以正式進入NIO的開發了

代碼

NIO

NIO服務端:只是接受客戶端發送過來的數據,然后打印在控制臺

/**
 * NIO
 * @author xuxiaobai
 */
public class NIOTest {
    private final static int port = 8080;

    public static void main(String[] args) throws IOException {
        //啟動服務端
        TCPServer();
    } 
	/**
     * TCP服務端
     * 接受TCP
     *
     * @throws IOException
     */
    public static void TCPServer() throws IOException {
        //創建服務端多路復用選擇器
        Selector selector = Selector.open();
        //創建服務端SocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //定義地址
        InetSocketAddress inetSocketAddress = new InetSocketAddress(InetAddress.getLocalHost(), port);
        //綁定地址
        serverSocketChannel.bind(inetSocketAddress);
        System.out.println("綁定成功:" + inetSocketAddress);
        //設置為非阻塞
        serverSocketChannel.configureBlocking(false);
        //注冊服務端選擇端,只接受accept事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            //加上延時,什么原理我忘記了,只知道是為了防止死鎖
            selector.select(500);
            //遍歷服務端選擇器的事件
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey next = iterator.next();
                if (!next.isValid()) {
                    //該key無效直接跳過
                    continue;
                }
                //注意
                if (next.isAcceptable()) {
                    //1. accept事件
                    //接收到accept事件,拿到channel,這個是服務端SocketChannel
                    ServerSocketChannel channel = (ServerSocketChannel) next.channel();
                    //accept得到連接客戶端的channel
                    SocketChannel accept = channel.accept();
                    accept.configureBlocking(false);
                    //注冊write事件
                    accept.register(selector, SelectionKey.OP_READ);
                    iterator.remove();
                } else if (next.isReadable()) {
                    //2. read事件
                    //開啟一個新的線程
                    Thread thread = new Thread(() -> {
                        SocketChannel channel = (SocketChannel) next.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                        byteBuffer.clear();
                        try {
                            channel.read(byteBuffer);
                            //開始處理數據
                            byteBuffer.flip();
                            byte[] bytes = new byte[byteBuffer.remaining()];
                            byteBuffer.get(bytes);
                            String x = new String(bytes);
                            if(x.equals("")){
                                //老是會莫名其妙地打印一些空行,打個補丁
                                return;
                            }
                            System.out.println(x);
                            if ("exit".equals(x)) {
                                //關閉通道
                                try {
                                    channel.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                                next.cancel();
                            }
                        } catch (IOException e) {
                            //出現異常的處理
                            e.printStackTrace();
                            try {
                                channel.close();
                            } catch (IOException ioe) {
                                ioe.printStackTrace();
                            }
                            next.cancel();
                        }

                    });
                    iterator.remove();
                    thread.start();
                }
            }
        }
    }
}

BIO

BIO服務端:接受客戶端的數據,然后打印在控制臺

BIO客戶端:向服務端發送數據。NIO的測試中也使用這個客戶端進行測試

/**
 * BIO
 * @author xuxiaobai
 */
public class BIOTest {
    private final static int port = 8080;

    public static void main(String[] args) throws IOException {
        TCPClient();
//        TCPServer();
    }

    /**
     * TCP客戶端
     * 發送TCP
     * @throws IOException
     */
    private static void TCPClient() throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        //定義地址
        InetSocketAddress inetSocketAddress = new InetSocketAddress(InetAddress.getLocalHost(), port);
        //連接
        socketChannel.connect(inetSocketAddress);
        System.out.println("連接成功:"+inetSocketAddress);
        Scanner scanner = new Scanner(System.in);
        while (true) {
            String next = scanner.next();
            //直接包裝一個buffer
            ByteBuffer wrap = ByteBuffer.wrap(next.getBytes());
            //寫入
            socketChannel.write(wrap);
            if ("exit".equals(next)) {
                //等于exit時關閉channel
                socketChannel.close();
                break;
            }
        }
    }

    /**
     * TCP服務端
     * 接受TCP
     * @throws IOException
     */
    private static void TCPServer() throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //定義地址
        InetSocketAddress inetSocketAddress = new InetSocketAddress(InetAddress.getLocalHost(), port);
        //綁定
        serverSocketChannel.bind(inetSocketAddress);
        System.out.println("綁定成功:"+inetSocketAddress);
        while (true) {
            //接受連接
            SocketChannel accept = serverSocketChannel.accept();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //定義一個緩沖區,讀出來的數據超出緩沖區的大小時會被丟棄
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    while (true) {
                        try {
                            //每次使用前都要清空,但這里沒有真的區clear數據,只是移動了buffer里面的下標
                            byteBuffer.clear();
                            //讀取數據到緩沖區
                            accept.read(byteBuffer);
                            //每次讀取數據前都要flip一下,這里都移動下標
                            byteBuffer.flip();
                            byte[] bytes = new byte[byteBuffer.remaining()];
                            //獲取數據
                            byteBuffer.get(bytes);
                            String x = new String(bytes);
                            System.out.println(x);
                            if (x.equals("exit")) {
                                //當讀出來的數據等于exit時退出
                                accept.close();
                                break;
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
            //啟動該線程
        }
    }
}

搞完了代碼,讓我們來看看代碼的演示效果————從客戶端發送數據到服務端,下面展示一下效果:

先后啟動BIO的TCPServer和TCPClient方法;

TCPClient:

Java:什么是NIO?什么是BIO?NIO 和 BIO 有什么區別?

 

TCPServer:

Java:什么是NIO?什么是BIO?NIO 和 BIO 有什么區別?

 

步驟

Java:什么是NIO?什么是BIO?NIO 和 BIO 有什么區別?

 

畫了個圖來表示,這是關于selector的配置流程,在循環中根據不同key值所進行的操作,跟上面文件復制的例子差不多了,只不過這里的Channel是通過 key.channel()獲得的。

 

差別

我們來看看一下BIO和NIO的差別。

BIO

我們用IDEA的debug啟動BIO的服務端,然后在啟動多個客戶端。

Java:什么是NIO?什么是BIO?NIO 和 BIO 有什么區別?

 

我這里啟動了三個客戶端,可以看到有三個線程已經創建好了,然而我這時還沒有發送數據到服務端。

NIO

我們用IDEA的debug啟動NIO的服務端,然后在啟動多個BIO客戶端。

Java:什么是NIO?什么是BIO?NIO 和 BIO 有什么區別?

 

這里啟動了多個客戶端,服務器上沒有多余的幾個線程。

修改BIO的TCPClient方法

    private static void TCPClient() throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        //定義地址
        InetSocketAddress inetSocketAddress = new InetSocketAddress(InetAddress.getLocalHost(), port);
        //連接
        socketChannel.connect(inetSocketAddress);
        System.out.println("連接成功:" + inetSocketAddress);
        Scanner scanner = new Scanner(System.in);
        while (true) {
            String next = scanner.next();
            //直接包裝一個buffer
//            ByteBuffer wrap = ByteBuffer.wrap(next.getBytes());
            //寫入
            while (true) {
                try {
                    //休眠
                  	//注意,休眠時間建議調高一點
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                socketChannel.write(ByteBuffer.wrap(next.getBytes()));
            }
//            if ("exit".equals(next)) {
//                //等于exit時關閉channel
//                socketChannel.close();
//                break;
//            }
        }
    }
復制代碼

休眠時間記得調高點!!!宕機警告!

這樣客戶端就會在讀取到第一次時,一直發送這個數據,可以看到一些線程,也是只有在收到數據之后才會創建這個線程去打印這個數據。如果休眠時間調高一點的話,就會看到有時候這里會一閃一閃的,調低后就會出現一閃而過的很多線程,如下圖。

Java:什么是NIO?什么是BIO?NIO 和 BIO 有什么區別?

 

四、總結

BIO的話,每次網絡請求過來之后,服務器都是會為這個請求創建一個線程,這個線程會一直等待這個請求后續的數據,等處理完成后才會銷毀這個線程;而NIO,當每次網絡請求過來時,服務器不會馬上創建一個線程去處理這個請求,而是會交給一個Selector線程,只有這個請求后續的數據全部傳輸過來后,Selector才會去通知其他其他線程或者創建一個線程來處理這個請求。

這就是NIO和BIO最大的差別,只有數據傳輸到服務器時才會讓線程去處理,減少了線程的空等待,大部分情況下可以采用線程池的方式來處理數據,可以提高線程的利用率。

分享到:
標簽:NIO
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定