最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中常見(jiàn)的文件拷貝方式小結(jié)

 更新時(shí)間:2024年03月26日 09:21:47   作者:碼頭的薯?xiàng)l  
這篇文章主要為大家詳細(xì)介紹了JAVA?四種拷貝文件的方式,分析一下他們對(duì)內(nèi)存使用的方式和各自應(yīng)用的場(chǎng)景,其實(shí)也是對(duì)之前學(xué)過(guò)的知識(shí)做一個(gè)回顧吧,快跟隨小編一起學(xué)習(xí)起來(lái)吧

1. 前言

閑話少敘,今天主要講講 JAVA 四種拷貝文件的方式,分析一下他們對(duì)內(nèi)存使用的方式和各自應(yīng)用的場(chǎng)景,其實(shí)也是對(duì)之前學(xué)過(guò)的知識(shí)做一個(gè)回顧吧,畢竟太久不回顧的話,記憶就像拼圖,隨著時(shí)間流逝就只剩下散落一地的碎片了。

2. 普通拷貝

protected void copyFile(File source, File target) {
    try (FileInputStream is = new FileInputStream(source);
         FileOutputStream os = new FileOutputStream(target);) {
         // 分配內(nèi)存空間
        byte[] buffer = new byte[4096];
        while (is.read(buffer) != -1) {
            os.write(buffer);
        }
    } catch (Exception e) {
        logger.error(e);
    }
}

第一種是最簡(jiǎn)單的,就是初始化一個(gè)輸入輸出流,然后在 JAVA 內(nèi)部分配一塊 4096 字節(jié)的內(nèi)存空間,然后不斷將文件寫(xiě)入這個(gè)內(nèi)存空間中,并輸出到指定文件。

但是需要注意的是,這樣的方式雖然簡(jiǎn)單,但是它的數(shù)據(jù)流實(shí)際上是經(jīng)過(guò)了 4 層傳輸?shù)?/p>

也就是我們的文件需要經(jīng)過(guò)內(nèi)核到我們 JAVA 虛擬機(jī)內(nèi)部的內(nèi)存 再到 內(nèi)核的 socket 緩沖區(qū),再到文件。

3. mmap 內(nèi)存映射的方式拷貝

protected void copyFile(File source, File target) {
    try (FileInputStream is = new FileInputStream(source);
         FileOutputStream os = new FileOutputStream(target);
         FileChannel ic = is.getChannel();
         FileChannel oc = os.getChannel();) {
        // 這里開(kāi)辟的內(nèi)存直接映射在內(nèi)核中
        ByteBuffer buffer = ByteBuffer.allocateDirect(4096);
        while (ic.read(buffer) != -1) {
            buffer.flip();
            oc.write(buffer);
            buffer.clear();
        }
    } catch (Exception e) {
        logger.error(e);
    }
}

第二種由于直接將內(nèi)存映射在了堆外,也就可以節(jié)省普通拷貝中第二步的過(guò)程,即不在需要將內(nèi)核緩沖區(qū)中的內(nèi)容再讀到給 java 虛擬機(jī)分配的內(nèi)存中了,比較適合需要 JAVA程序進(jìn)行文件處理,或者一些小文件的傳輸

或者也可以通過(guò)封裝好的

new RandomAccessFile(file, "r").getChannel().map(FileChannel.MapMode.READ_ONLY, 0, 1024);

來(lái)實(shí)現(xiàn),底層是通過(guò) directByteBufferConstructor.newInstance 分配堆外內(nèi)存來(lái)實(shí)現(xiàn)的。

4. 零拷貝 sendFile 方式實(shí)現(xiàn)

protected void copyFile(File source, File target) {
    try {
        if (!target.exists()) {
            target.createNewFile();
        }
    } catch (Exception e) {
        logger.error(e);
    }
    try (FileChannel is = new RandomAccessFile(source, "r").getChannel();
        FileChannel os = new RandomAccessFile(target, "rw").getChannel()) {
        is.transferTo(0, source.length(), os);
    } catch (Exception e) {
        logger.error(e);
    }
}

第三種其實(shí)也就是我們俗稱(chēng)的零拷貝的方式,在 Linux 2.1 版本中,引入了 sendFile 方法,也就是可以跳過(guò)用戶空間直接實(shí)現(xiàn)傳輸,java 程序中通過(guò) 新io 中 file 的 transformTo 方法,底層調(diào)用 liunx 內(nèi)核級(jí)的 sendFile 方法,將內(nèi)核數(shù)據(jù)直接拷貝到了 socket 緩沖區(qū),從而節(jié)省了拷貝次數(shù)和消耗。

既然第三種方式相對(duì)于第一種和第二種來(lái)說(shuō),可以完全不經(jīng)過(guò) java 應(yīng)用程序,為什么不都直接都用第三種就好了呢?

正是因?yàn)樗耆唤?jīng)過(guò) java 程序,也就是說(shuō)我們無(wú)法對(duì)文件內(nèi)容進(jìn)行二次修改了,第三種方式比較適用于我們將無(wú)需經(jīng)過(guò)程序處理的大文件。

需要注意的是,之所以會(huì)有程序的內(nèi)存空間和內(nèi)核的內(nèi)存空間的區(qū)別,其實(shí)主要就是為了隔離,防止惡意程序可以直接訪問(wèn)內(nèi)核的內(nèi)存空間

5. 多線程的方式實(shí)現(xiàn)拷貝

// 定義一個(gè)線程的數(shù)量
private Integer threadCount = 5;

protected void copyFile(File source, File target) {
    long workLoad = source.length() / threadCount;
    for (Integer i = 0; i < threadCount; i++) {
        ThreadFileRunnable threadFileRunnable = new ThreadFileRunnable(source, target, i * workLoad, workLoad);
        new Thread(threadFileRunnable, "copy-thread" + i).start();
    }
}

private class ThreadFileRunnable implements Runnable {
    private File source;
    private File target;
    // 定義每個(gè)線程開(kāi)始復(fù)制時(shí)跳過(guò)的字節(jié)長(zhǎng)度和工作負(fù)載大小
    private long skipLen;
    private long workLoad;
    // 定義IO操作的單位大小,這里設(shè)置為1024字節(jié)
    private final int IO_UNIT = 1024;

    public ThreadFileRunnable (File source, File target, long skipLen, long workLoad) {
        this.source = source;
        this.target = target;
        this.skipLen = skipLen;
        this.workLoad = workLoad;
    }

    @Override
    public void run() {
        try {
            try (FileInputStream is = new FileInputStream(this.source);
                 BufferedInputStream bis = new BufferedInputStream(is);
                 // 創(chuàng)建目標(biāo)文件的RandomAccessFile,以讀寫(xiě)模式打開(kāi)
                 RandomAccessFile rof = new RandomAccessFile(this.target, "rw");) {
                // 跳過(guò)指定偏移量
                bis.skip(this.skipLen);
                // 將讀寫(xiě)指針移動(dòng)到指定偏移量
                rof.seek(this.skipLen);
                byte[] bytes = new byte[IO_UNIT];
                // 計(jì)算需要進(jìn)行的IO操作次數(shù)
                long io_num = this.workLoad / IO_UNIT + 1;
                // 如果工作負(fù)載大小能被IO_UNIT整除,則IO操作次數(shù)減1
                if (this.workLoad % IO_UNIT == 0) {
                    io_num--;
                }
                int count = bis.read(bytes);
                while (io_num != 0) {
                    rof.write(bytes,0,count);
                    count = bis.read(bytes,0,count);
                    io_num--;
                }
            }
        } catch (Exception e) {
            // 捕獲并打印異常信息
            e.printStackTrace();
        }
    }
}

第四種如果文件特別大的時(shí)候,我們還可以通過(guò)多線程的方式來(lái)進(jìn)行文件的讀寫(xiě),可以充分利用 CPU 多核效率來(lái)進(jìn)一步提升文件的處理效率。

5. 總結(jié)

對(duì)于 JAVA 文件拷貝來(lái)說(shuō),本文只是展示和介紹了冰山一角,實(shí)際上對(duì)于讀寫(xiě)流操作,操作系統(tǒng)的實(shí)現(xiàn)經(jīng)過(guò)了長(zhǎng)時(shí)間的演化,從 CPU 中斷pagecache,從 sendFileDMA,以及網(wǎng)絡(luò)傳輸過(guò)程中的 bio nio pollepoll,操作系統(tǒng)經(jīng)過(guò)很多年的演化其中文件和網(wǎng)絡(luò)的傳輸處理的復(fù)雜程度可想而知。

到此這篇關(guān)于Java中常見(jiàn)的文件拷貝方式小結(jié)的文章就介紹到這了,更多相關(guān)Java文件拷貝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot+jpa配置如何根據(jù)實(shí)體類(lèi)自動(dòng)創(chuàng)建表

    SpringBoot+jpa配置如何根據(jù)實(shí)體類(lèi)自動(dòng)創(chuàng)建表

    這篇文章主要介紹了SpringBoot+jpa配置如何根據(jù)實(shí)體類(lèi)自動(dòng)創(chuàng)建表,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java JDK 動(dòng)態(tài)代理的使用方法示例

    Java JDK 動(dòng)態(tài)代理的使用方法示例

    Java 動(dòng)態(tài)代理機(jī)制以巧妙的方式近乎完美地實(shí)踐了代理模式的設(shè)計(jì)理念。下面這篇文章主要給大家分享了關(guān)于Java JDK 動(dòng)態(tài)代理的使用方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-07-07
  • Java簡(jiǎn)單實(shí)現(xiàn)session保存到redis的方法示例

    Java簡(jiǎn)單實(shí)現(xiàn)session保存到redis的方法示例

    這篇文章主要介紹了Java簡(jiǎn)單實(shí)現(xiàn)session保存到redis的方法,結(jié)合實(shí)例形式分析了Java將session存入redis緩存服務(wù)器的相關(guān)設(shè)置、實(shí)現(xiàn)技巧與操作注意事項(xiàng),需要的朋友可以參考下
    2018-05-05
  • Java CAS原子操作詳解

    Java CAS原子操作詳解

    在synchronized的優(yōu)化過(guò)程中我們看到大量使用了CAS操作,CAS全稱(chēng)Compare And Set(或Compare And Swap),簡(jiǎn)單來(lái)說(shuō)CAS操作就是一個(gè)虛擬機(jī)實(shí)現(xiàn)的原子操作
    2023-02-02
  • Vue3源碼解讀effectScope API及實(shí)現(xiàn)原理

    Vue3源碼解讀effectScope API及實(shí)現(xiàn)原理

    這篇文章主要為大家介紹了Vue3源碼解讀effectScope API及實(shí)現(xiàn)原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Java多線程中的Phaser詳解

    Java多線程中的Phaser詳解

    這篇文章主要介紹了Java多線程中的Phaser詳解,Pahser是一個(gè)可以重復(fù)使用的同步屏障,Phaser是按照不同階段執(zhí)行線程的,它本身維護(hù)著一個(gè)叫 phase 的成員變量代表當(dāng)前執(zhí)行的階段,需要的朋友可以參考下
    2023-11-11
  • jstack報(bào)錯(cuò)Unable to open socket file解決

    jstack報(bào)錯(cuò)Unable to open socket file解決

    這篇文章主要為大家介紹了jstack報(bào)錯(cuò)Unable to open socket file的解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-02-02
  • 詳解Java使用Pipeline對(duì)Redis批量讀寫(xiě)(hmset&hgetall)

    詳解Java使用Pipeline對(duì)Redis批量讀寫(xiě)(hmset&hgetall)

    本篇文章主要介紹了Java使用Pipeline對(duì)Redis批量讀寫(xiě)(hmset&hgetall),具有一定的參考價(jià)值,有興趣的可以了解一下。
    2016-12-12
  • 詳談Map的key、value值的數(shù)據(jù)類(lèi)型不能為基本類(lèi)型的原因

    詳談Map的key、value值的數(shù)據(jù)類(lèi)型不能為基本類(lèi)型的原因

    這篇文章主要介紹了詳談Map的key、value值的數(shù)據(jù)類(lèi)型不能為基本類(lèi)型的原因,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • Java中的OneToMany的使用方法

    Java中的OneToMany的使用方法

    這篇文章主要介紹了Java中的OneToMany的使用方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04

最新評(píng)論

三都| 东宁县| 镇安县| 聂荣县| 蒙城县| 景宁| 海南省| 苏尼特右旗| 定结县| 潜山县| 日照市| 湟中县| 乌兰浩特市| 方城县| 大安市| 准格尔旗| 屏山县| 奎屯市| 右玉县| 教育| 神木县| 金坛市| 英吉沙县| 高邮市| 赞皇县| 山西省| 喀喇沁旗| 库车县| 望城县| 徐闻县| 额尔古纳市| 石狮市| 琼中| 英德市| 昌平区| 邛崃市| 常州市| 嵩明县| 盖州市| 财经| 高陵县|