NIO基礎(chǔ)操作

原文鏈接http://zhhll.icu/2020/java%E5%9F%BA%E7%A1%80/IO/NIO%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/

NIO

同步非阻塞

阻塞與非阻塞的區(qū)別:

  • 阻塞時,在調(diào)用結(jié)果返回時怠晴,當前線程會被掛起仆邓,并在得到結(jié)果之后返回
  • 非阻塞時古程,如不能立即得到結(jié)果谈截,該調(diào)用不會阻塞當前線程玖院,調(diào)用者需要定時輪詢查看處理狀態(tài)

Channel(通道)和Buffer(緩沖區(qū))

與普通IO的不同和關(guān)系

  • NIO以塊的方式處理數(shù)據(jù)台舱,但是IO是以最基礎(chǔ)的字節(jié)流的形式去寫入和讀出的

  • NIO不再是和IO一樣用OutputStream和InputStream輸入流的形式來進行處理數(shù)據(jù)的律杠,但是又是基于這種流的方式,采用了通道和緩沖區(qū)的形式進行處理

  • NIO的通道是可以雙向的竞惋,IO的流只能是單向的

  • NIO的緩沖區(qū)(字節(jié)數(shù)組)還可以進行分片柜去,可以建立只讀緩沖區(qū)、直接緩沖區(qū)和間接緩沖區(qū)拆宛,只讀緩沖區(qū)就是只可以讀嗓奢,直接緩沖區(qū)是為了加快I/O速度,以一種特殊的方式分配其內(nèi)存的緩沖區(qū)

  • NIO采用的是多路復(fù)用的IO模型浑厚,BIO用的是阻塞的IO模型

通道的概念

通道是對原I/O包中的流的模擬股耽。到任何目的地的所有數(shù)據(jù)都必須通過一個Channel對象(通道)。一個Buffer實質(zhì)上就是一個容器對象钳幅。發(fā)送給一個通道的所有對象都必須首先放到緩沖區(qū)中物蝙;從通道中讀取的任何數(shù)據(jù)都要讀到緩沖區(qū)中

緩沖區(qū)的概念

  • Buffer是一個對象,它包含一些要寫入或者剛讀出的數(shù)據(jù)敢艰。在NIO中加入Buffer對象诬乞,在流式IO中,將數(shù)據(jù)直接寫入或者讀到Stream對象中

  • 在NIO庫中钠导,所有數(shù)據(jù)都是用緩沖區(qū)處理的震嫉。在讀取數(shù)據(jù)時,它是直接讀到緩沖區(qū)中的牡属。在寫入數(shù)據(jù)時票堵,它是寫入到緩沖區(qū)的。任何時候訪問NIO中的數(shù)據(jù)逮栅,都需要將它放到緩沖區(qū)中

  • 緩沖區(qū)實質(zhì)上是一個數(shù)組悴势。通常它是一個字節(jié)數(shù)組窗宇,但是也可以使用其他種類的數(shù)組。但是一個緩沖區(qū)不僅僅是一個數(shù)組瞳浦,緩沖區(qū)提供了對數(shù)據(jù)的結(jié)構(gòu)化訪問,而且還可以跟蹤系統(tǒng)的讀/寫進程

ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer

選擇器

Selector是多路復(fù)用器废士,用于同時檢測多個通道的事件以實現(xiàn)異步I/O叫潦。

通過一個選擇器來同時對多個套接字通道進行監(jiān)聽,當套接字通道有可用的事件的時候官硝,通道改為可用狀態(tài)矗蕊,選擇器就可以實現(xiàn)可用的狀態(tài)。

工作原理

客戶端-----》Channel-----》Selector------》keys--狀態(tài)改變---》server

Buffer 緩沖區(qū)
Channel 通道
Selector 選擇器

Server端創(chuàng)建ServerSocketChannel 有一個Selector多路復(fù)用器 輪詢所有注冊的通道氢架,根據(jù)通道狀態(tài)傻咖,執(zhí)行相關(guān)操作

  • Connect 連接狀態(tài)
  • Accept 阻塞狀態(tài)
  • Read 可讀狀態(tài)
  • Write 可寫狀態(tài)

Client端創(chuàng)建SocketChannel 注冊到Server端的Selector

buffer

  • capacity 緩沖區(qū)數(shù)組的總長度
  • position 下一個要操作的數(shù)據(jù)元素的位置
  • limit 緩沖區(qū)數(shù)組中不可操作的下一個元素的位置,limit<=capacity
  • mark 用于記錄當前position的前一個位置或者默認是0
  • clear/flip/rewind等都是操作limit和position的值來實現(xiàn)重復(fù)讀寫的

ByteBuffer

有且僅有ByteBuffer(字節(jié)緩沖區(qū))可以直接與通道交互岖研。

public static void main(String[] args) {
        //生成FileChannel文件通道  FileChannel的操作--> 操作ByteBuffer用于讀寫卿操,并獨占式訪問和鎖定文件區(qū)域


        // 寫入文件
        try(FileChannel fileChannel = new FileOutputStream(FILE).getChannel()){
            fileChannel.write(ByteBuffer.wrap("test".getBytes()));
        } catch (IOException e){
            throw new RuntimeException("寫入文件失敗",e);
        }
        // 在文件結(jié)尾寫入
        try(FileChannel fileChannel = new RandomAccessFile(FILE,"rw").getChannel()){
            fileChannel.position(fileChannel.size());//移至文件結(jié)尾
            fileChannel.write(ByteBuffer.wrap("some".getBytes()));
        } catch (IOException e){
            throw new RuntimeException("寫入文件結(jié)尾失敗",e);
        }

        try(FileChannel fileChannel = new FileInputStream(FILE).getChannel();
            FileChannel out = new FileOutputStream("C:\\Users\\sinosoft\\Desktop\\copy.txt").getChannel()
        ){
            // 讀取操作,需要調(diào)用allocate顯示分配ByteBuffer
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            // read之后將數(shù)據(jù)放入緩沖區(qū)
            while (fileChannel.read(byteBuffer) != -1){
                byteBuffer.flip(); // 準備寫入
                out.write(byteBuffer);
                byteBuffer.clear(); // 清空緩存區(qū)
            }
        } catch (IOException e){
            throw new RuntimeException("讀取文件失敗",e);
        }
    }

方法說明

rewind()方法是將position設(shè)置為緩沖區(qū)的開始位置

get()和put()都會修改position

get(int)和put(int)都不會修改position

mark()設(shè)置mark為當前position

flip()將寫模式切換為讀模式

public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

內(nèi)存映射文件

內(nèi)存映射文件可以創(chuàng)建和修改那些因為太大而無法放入內(nèi)存的文件孙援。

RandomAccessFile tdat = new RandomAccessFile("test.dat", "rw");
MappedByteBuffer out = tdat.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, length);

或者
FileChannel fc = new FileInputStream(new File("temp.tmp")).getChannel();
IntBuffer ib = fc.map(FileChannel.MapMode.READ_ONLY,0, fc.size()).asIntBuffer();

映射文件訪問比標準IO性能高很多

文件鎖定

文件鎖定可同步訪問害淤,文件鎖對其他操作系統(tǒng)進程可見,因為java文件鎖直接映射到本機操作系統(tǒng)鎖定工具拓售。

public class FileLockTest {
    private static final String FILE = "C:\\Users\\sinosoft\\Desktop\\剩余工作副本.txt";
    public static void main(String[] args) throws IOException, InterruptedException {
        FileChannel fileChannel = new FileOutputStream(FILE).getChannel();

        // 文件鎖
        FileLock fileLock = fileChannel.tryLock();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                FileChannel fileChannel = null;
                try {
                    fileChannel = new FileOutputStream(FILE).getChannel();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                byteBuffer.put("aqws".getBytes());
                try {
                    System.out.println("線程準備寫");
                    fileChannel.write(byteBuffer);
                    System.out.println("線程寫完");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        if(fileLock != null){
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            byteBuffer.put("aqwqdqdhwfwihfejfhi".getBytes());
            System.out.println("主線程睡眠");
            Thread.sleep(10000);
            // 會報錯 java.nio.channels.NonWritableChannelException
//            fileChannel.read(byteBuffer);
            System.out.println("主線程準備寫");
            fileChannel.write(byteBuffer);
            fileLock.release();
        }
    }
}



主線程睡眠
線程準備寫
java.io.IOException: 另一個程序已鎖定文件的一部分窥摄,進程無法訪問。
    at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
    at sun.nio.ch.FileDispatcherImpl.write(FileDispatcherImpl.java:75)
    at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
    at sun.nio.ch.IOUtil.write(IOUtil.java:65)
    at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:211)
    at com.zhanghe.study.io.nio.FileLockTest$1.run(FileLockTest.java:35)
    at java.lang.Thread.run(Thread.java:745)
主線程準備寫
                                                                                     

通過調(diào)用FileChannel上的tryLock或lock础淤,可以獲得整個文件的FileLock(SocketChannel崭放、DatagramChannel和ServerSocketChannel不需要鎖定,因為本質(zhì)上就是單線程實體)

tryLock()是非阻塞的鸽凶,試圖獲取鎖币砂,若不能獲取,只是從方法調(diào)用返回

lock()會阻塞玻侥,直到獲得鎖道伟,或者調(diào)用lock()的線程中斷,或者調(diào)用lock()方法的通道關(guān)閉使碾。

使用FileLock.release()釋放鎖

// 鎖定文件的一部分蜜徽,鎖住size-position區(qū)域。第三個參數(shù)指定是否共享此鎖
tryLock(long position, long size, boolean shared)

由于本身的博客百度沒有收錄票摇,博客地址http://zhhll.icu

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拘鞋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子矢门,更是在濱河造成了極大的恐慌盆色,老刑警劉巖灰蛙,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異隔躲,居然都是意外死亡摩梧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門宣旱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仅父,“玉大人,你說我怎么就攤上這事浑吟◇舷耍” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵组力,是天一觀的道長省容。 經(jīng)常有香客問我,道長燎字,這世上最難降的妖魔是什么腥椒? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮候衍,結(jié)果婚禮上寞酿,老公的妹妹穿的比我還像新娘。我一直安慰自己脱柱,他們只是感情好伐弹,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著榨为,像睡著了一般惨好。 火紅的嫁衣襯著肌膚如雪列赎。 梳的紋絲不亂的頭發(fā)上仰剿,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音叮阅,去河邊找鬼矩乐。 笑死龄句,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的散罕。 我是一名探鬼主播分歇,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼欧漱!你這毒婦竟也來了职抡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤误甚,失蹤者是張志新(化名)和其女友劉穎缚甩,沒想到半個月后谱净,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡擅威,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年壕探,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片郊丛。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡李请,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宾袜,到底是詐尸還是另有隱情捻艳,我是刑警寧澤驾窟,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布庆猫,位于F島的核電站,受9級特大地震影響绅络,放射性物質(zhì)發(fā)生泄漏月培。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一恩急、第九天 我趴在偏房一處隱蔽的房頂上張望杉畜。 院中可真熱鬧,春花似錦衷恭、人聲如沸此叠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灭袁。三九已至,卻和暖如春窗看,著一層夾襖步出監(jiān)牢的瞬間茸歧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工显沈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留软瞎,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓拉讯,卻偏偏與公主長得像涤浇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子魔慷,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容