文件操作

 

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.reflect.Field;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Stream;

import com.alibaba.fastjson.JSON;
import com.sun.istack.internal.NotNull;

public class NioFileOperateUtils {
    /**
     * 获取最大堆内存,单位byte
     * @return
     */
    public long getMaxJvmSpace() {
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage heapMemory= memoryMXBean.getHeapMemoryUsage();
        long maxHeap = heapMemory.getMax();
        return maxHeap;
    }
    
    
    /**
     * 获取最大堆外内存(单位byte),没有设置过默认和堆内大小一样
     * @return
     */
    public long getMaxDirectMemory(){
        try {
            //不能直接获取类对象,只能通过反射
            Class<?> c = Class.forName("java.nio.Bits");
            Field maxMemory = c.getDeclaredField("maxMemory");
            maxMemory.setAccessible(true);
            Field reservedMemory = c.getDeclaredField("reservedMemory");
            reservedMemory.setAccessible(true);
            //最大堆外内存
            Long maxMemoryValue = (Long)maxMemory.get(null);
            //已分配的堆外内存大小
//            Object reservedMemoryValue = reservedMemory.get(null);
//            System.out.println(reservedMemoryValue);
            
            return maxMemoryValue;
        }catch(Exception e) {
            e.printStackTrace();
        }
        return 0;
    }
    
    /**
     * java8 nio文件流读取(基于io的Reader转BufferedReader封装实现),依赖堆内存
     * @param filePath
     * @return
     */
    public List<String> readTextFile(String filePath) {
        Stream<String> stream=null;
        try {
            //默认utf-8编码
            stream=Files.lines(Paths.get(filePath));
            List<String> result=new ArrayList<>();
            stream.forEach(str->result.add(str));
            //stream.forEach(result::add);等同于上面
            return result;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            if(null!=stream) {
                stream.close();
            }
        }
        
        
        return null;
    }
    
    /**
     * 通过MappedByteBuffer映射分段读取文件数据
     * @param filePath 文件地址
     * @param threadNums 分段数量
     * @return
     */
    public ConcurrentLinkedDeque<String> readBigTextFile(String filePath,int threadNums){
        File file =new File(filePath);
        if(!file.exists()) {
            return null;
        }
        //大文件转字节码存储容器
        RandomAccessFile randomAccessFile=null;
        //文件通道
        FileChannel fileChannel=null;
        //线程池
        ExecutorService executor=null;
        //线程安全队列
        ConcurrentLinkedDeque<String> contents=new ConcurrentLinkedDeque<String>();
        try {
            randomAccessFile=new RandomAccessFile(file, "rw");
             fileChannel=randomAccessFile.getChannel();
            //不分页处理
            if(threadNums<2) {
                getChanelFileContent(contents, fileChannel, 0, file.length(),file.length());
                return contents;
            }
            //分页采用多线程处理
            executor=Executors.newFixedThreadPool(threadNums);
            //发令枪,并行执行
            CountDownLatch latch=new CountDownLatch(threadNums);
            // 每线程应该读取的字节数
            long numPerThread = file.length() / threadNums;
            // 整个文件整除后剩下的余数
            long left = file.length() % threadNums;
            
            for(int i=0;i<threadNums;i++) {
                if (i == threadNums - 1 ) {
                    executor.execute(new ReadRunable(fileChannel, i*numPerThread, numPerThread+left,file.length(), contents,latch));
                }else {
                    executor.execute(new ReadRunable(fileChannel, i*numPerThread, numPerThread,file.length(), contents,latch));
                }
                //发令枪减数量,直到减为0执行时,所有阻塞的线程同时并行执行
                latch.countDown();
            }
            //需要线程等待会,不然FileLock文件锁可能还没释放会报错
            Thread.sleep(500);
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            if(null!=executor) {
                executor.shutdown();
            }
            if(null!=fileChannel) {
                try {
                    fileChannel.close();
                    
                } catch (IOException e) {
                    
                    e.printStackTrace();
                }
            }
            if(null!=randomAccessFile) {
                try {
                    randomAccessFile.close();
                } catch (IOException e) {
                    
                    e.printStackTrace();
                }
            }
            
        }
        
        return contents;
    }
    
    
    class ReadRunable implements  Runnable {
        private FileChannel fileChannel;//文件通道
        private long position;//字节码起始位置
        private long size;//预定义读取字节码的个数
        private long fileSize;//文件总字节码大小
        private CountDownLatch latch;//线程并行执行发令枪
        
        private ConcurrentLinkedDeque<String> contents;
        
        public ReadRunable(FileChannel fileChannel, long position, long size,long fileSize, ConcurrentLinkedDeque<String> contents,CountDownLatch latch) {
            super();
            this.fileChannel = fileChannel;
            this.position = position;
            this.size = size;
            this.contents = contents;
            this.fileSize=fileSize;
            this.latch=latch;
        }

        @Override
        public void run() {
            try {
                latch.await();//当前线程等待发令枪唤醒
            } catch (InterruptedException e) {
                
                e.printStackTrace();
            }
            getChanelFileContent(contents, fileChannel, position, size,fileSize);
        }
    }
    
    /**
     *
     * @param contents 接收文件每行的数据
     * @param file 文件
     * @param position 起始字符位置
     * @param size 读取的字符长度范围
     */
    private void getChanelFileContent(@NotNull ConcurrentLinkedDeque<String> contents,@NotNull FileChannel fileChannel,long position,long size,long fileSize){
        
        FileLock   lock=null;
        boolean isLast= position+size >= fileSize;
        //真的读取的数据,字符是\n后的下一字节位置开始读
        long realPosition=position;
        //真的读取数据的大小,在遇到\r\n后的字节位置
        long realSize=size;
        //开始位置和结束位置是否为换行符号
        boolean newLine=false;
        try {
                
                 MappedByteBuffer mapbuffer=null;
                 byte line="\n".getBytes()[0];
                 byte getLine;
                 //到下一个\n字节还有几个长度
                 int addNum=0;
                 if(position > 0) {
                     addNum=0;
                     newLine=false;
                    //开始字节的前一位是否是换行符
                     mapbuffer=fileChannel.map(FileChannel.MapMode.READ_ONLY, position-1, 1);
                     getLine=mapbuffer.get();
                     if(line==getLine) {
                         newLine=true;
                         mapbuffer.clear();
                     }
                    
                    while(!newLine) {
                         mapbuffer=fileChannel.map(FileChannel.MapMode.READ_ONLY, position+addNum, 1);
                         getLine=mapbuffer.get();
                         if(line==getLine) {
                             mapbuffer.clear();
                             break;
                         }else if("\r".getBytes()[0]==getLine) {
                             addNum=addNum+1;
                             mapbuffer.clear();
                             break;
                         }
                         addNum++;
                     }
                    //开始字节码位置在\n后+1
                    realPosition=realPosition+addNum+1;
                    
                }

                //是否为换行符号
                newLine=false;
                //从新赋值0
                 addNum=0;
                 while(!isLast && !newLine) {
                     if(null!=mapbuffer) {
                         mapbuffer.clear();
                     }
                    //结尾字节码判断起始位置,不是最后一页采用上面计算后真实的开始位置+原始长度;
                     mapbuffer=fileChannel.map(FileChannel.MapMode.READ_ONLY, realPosition+size+addNum, 1);
                     getLine=mapbuffer.get();
                     if(line==getLine) {
                         mapbuffer.clear();
                         break;
                     }else if("\r".getBytes()[0]==getLine) {
                         addNum=addNum+1;
                         mapbuffer.clear();
                         break;
                     }
                     addNum++;
                 }
                //结束字节码位置在\n
                realSize=isLast ? fileSize-realPosition : realSize+addNum;

                //锁定文件位置范围
              lock=fileChannel.lock(realPosition, realSize, false);
              mapbuffer=fileChannel.map(FileChannel.MapMode.READ_ONLY, realPosition, realSize);
              String strcontens = Charset.forName("UTF-8").decode(mapbuffer).toString();
              
              //根据换行\r\n符号获取每行的数据
              StringTokenizer token = new StringTokenizer(strcontens,"\r\n");
              while(token.hasMoreTokens()) {
                  contents.add(token.nextToken());
              }
              mapbuffer.clear();
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            if(null!=lock) {
                try {
                    lock.release();
                } catch (IOException e) {
                    
                    e.printStackTrace();
                }
            }
            
        }    
    }
    
    public static void main(String[] args) {
        
        //ByteBuffer.allocateDirect(10 * 1024 * 1024);
        NioFileOperateUtils test=new NioFileOperateUtils();
//        System.out.println("堆内存:"+(test.getMaxJvmSpace()/(1024*1024*1024)));
//        System.out.println("堆外内存:"+test.getMaxDirectMemory());
//        System.out.println("start###"+System.currentTimeMillis());
//        List<String> strList=test.readTextFile("F:/test.txt");
//        System.out.println(JSON.toJSONString(strList));
//        System.out.println("end###"+System.currentTimeMillis());
        System.out.println("start###"+System.currentTimeMillis());
        ConcurrentLinkedDeque<String> list=test.readBigTextFile("F:/test.txt",8);
        System.out.println(JSON.toJSONString(list));
        System.out.println("end###"+System.currentTimeMillis());
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值