美文网首页Android开发Android技术知识Android开发
JAVA多线程读写文件如何做到线程安全?(文件锁,FileCha

JAVA多线程读写文件如何做到线程安全?(文件锁,FileCha

作者: Vghh | 来源:发表于2019-08-17 01:25 被阅读25次

    @[toc]

    NIO提升性能

    在JAVA的标准I/O中,提供了基于流的I/O实现,即InputStream和OutputStream。这种基于流的实现以字节为单位处理数据。NIO是New I/O的简称,表示一套新的JAVA I/O标准。在Jdk 1.4中开始引入,它具有以下特性:

    • 为所有的原始类型提供(Buffer)缓存支持;
    • 使用Java.nio.charset.Charset作为字符集编码解码解决方案;
    • 增加通道(Cahnnel)对象,作为新的原始I/O抽象;
    • 支持锁和内存映射文件的文件访问接口;
    • 提供了基于Selector的异步网络I/O。

    与流式的I/O不容,NIO是基于块(Block)的,它以块为基本单位处理数据。在NIO中,最为重要的2个组件是缓冲Buffer和通道Channel。缓冲是一块连续的内存块,是NIO读写数据的中转地。通道表示缓冲数据的源头或者目的地,它用于向缓冲读取或者写入数据,是访问缓冲的接口。

    在这里插入图片描述

    多线程读写同一个文件有哪些场景需要同步处理?

    • 有线程正在读文件,另开辟线程写文件;
    • 有线程正在写文件,另开辟线程读文件;
    • 有线程正在写文件,另开辟线程写文件

    总之,读写互斥,写读互斥,写写互斥,只有读读相容(可以异步)。

    使用对文件加锁的方式做到线程安全

    FileInputStream、FileOutputStream、RandomAccessFile均可得到FileChannel对象,对文件锁进行操作。
    独占锁tryLock()

    FileChannel的tryLock()是非阻塞的,也就是说,在发现文件被锁住的时候,直接返回null,并且抛出异常,如果没有锁住,直接返回该文件的文件锁。
    它是独占锁,就是只能被一个线程持有,它能禁止其他线程获取共享锁,可用于写文件。

      while (true) {
                    try {
                        fileLock = fileChannel.tryLock();//独占锁
                        break;
                    } catch (Exception e) {
                        System.out.println("有其他线程正在操作该文件,当前线程" + Thread.currentThread().getName());
                    }
                }
    

    共享锁tryLock(0, Long.MAX_VALUE, true)

    FileChannel的tryLock(0, Long.MAX_VALUE, true)是非阻塞的,在发现文件被锁住的时候,直接返回null,并且抛出异常,如果没有锁住,直接返回该文件的文件锁。
    它是共享锁,能被多个线程同时持有,它能禁止其他线程获取独占锁,可用于读文件。

    while (true) {
                    try {
                        fileLock = fileChannel.tryLock(0, Long.MAX_VALUE, true);//共享锁
                        break;
                    } catch (Exception e) {
                        System.out.println("有其他线程正在操作该文件,当前线程" + Thread.currentThread().getName());
                    }
                }
    

    独占锁lock()

    而FileChannel的lock()是阻塞的,在文件被锁定的情况下,会保持阻塞,直到获得该锁为止。

    fileLock = fileChannel.lock();
    ......
    

    写文件线程安全

     /**
         * 将str写入文件,同步操作,独占锁
         */
        public void writeStr2ReplaceFileSync(String str, String pathFile, IOListener ioListener) {
            File file;
            try {
                file = FileUtils.createFile(pathFile);
            } catch (IOException e) {
                e.printStackTrace();
                ioListener.onFail("文件创建失败,请检查路径是否合法以及读写权限");
                return;
            }
            FileOutputStream fileOutputStream = null;
            FileChannel fileChannel = null;
            FileLock fileLock = null;//文件锁
            try {
    
                /**
                 * 写文件
                 */
                fileOutputStream = new FileOutputStream(file);
                fileChannel = fileOutputStream.getChannel();
                while (true) {
                    try {
                        fileLock = fileChannel.tryLock();//独占锁
                        break;
                    } catch (Exception e) {
                        System.out.println("有其他线程正在操作该文件,当前线程" + Thread.currentThread().getName());
                    }
                }
                if (fileLock != null) {
                    int len = 0;
                    long current = file.length();
                    if (isRunning ) {
                        fileChannel.write(ByteBuffer.wrap(str.getBytes()));
                        current += len;
                        LogUtils.log("当前线程" + Thread.currentThread().getName());
                        ioListener.onLoading(str.getBytes(), current, str.length());
                    }else {
                        //中断
                        ioListener.onInterrupted();
                    }
                    if (fileLock != null && fileLock.isValid()) {
                        LogUtils.log("release-write-lock");
                        fileLock.release();
                    }
                    close(fileChannel);
                    close(fileOutputStream);
                    if (file.length() == str.getBytes().length) {
                        ioListener.onCompleted(file);
                    }
                }
    
            } catch (IOException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
            } finally {
            }
        }
    

    读文件线程安全

      /**
         * 同步读取,共享锁,但无法同时进行写操作
         *
         * @param ioListener
         */
        public void read2StrSync(String pathFile, IOListener ioListener) {
            FileInputStream fileInputStream = null;
            FileChannel fileChannel = null;
            FileLock fileLock = null;//文件锁
    
            ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
            try {
    
                /**
                 * 读文件
                 */
    
                fileInputStream = new FileInputStream(pathFile);
                fileChannel = fileInputStream.getChannel();
                while (true) {
                    try {
                        fileLock = fileChannel.tryLock(0, Long.MAX_VALUE, true);//共享锁
                        break;
                    } catch (Exception e) {
                        System.out.println("有其他线程正在操作该文件,当前线程" + Thread.currentThread().getName());
                    }
                }
                if (fileLock != null) {
    
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    int len = 0;
                    long current = 0;
                    while (isRunning && (len = fileChannel.read(byteBuffer)) != -1) {
                        //0,byteBuffer.position(),必须写这个,否则GG,读取文件错乱
                        byteArrayOutputStream.write(byteBuffer.array(),0,byteBuffer.position());
                        current += len;
                        ioListener.onLoading("", current, fileChannel.size());
                        byteBuffer.clear();
                    }
                    if (fileLock != null && fileLock.isValid()) {
                    LogUtils.log("release-read-lock");
                        fileLock.release();
                    }
                    close(fileChannel);
                    close(fileInputStream);
                    //中断
                    if (len != -1) {
                        ioListener.onInterrupted();
                    } else {
                        ioListener.onCompleted(byteArrayOutputStream.toString("utf-8"));
    
                    }
                }
    
    
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
            } catch (IOException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
    
            } finally {
    
    
            }
        }
    

    小编写的IOListener接口,用于回调

    public interface IOListener<T> {
        public void onCompleted(T result);
        public void onLoading(T readedPart, long current, long length);
        public void onInterrupted();
        public void onFail(String errorMsg);
    }
    
    

    小编写的IOUtils工具类,专门用于文件读写,流的读写

    public class IOUtils {
        private boolean isRunning = true;
    
        private long contentLength = 0;
        private String encodeType = "utf-8";
    
        public IOUtils() {
            isRunning = true;
        }
    
        public IOUtils setContentLength(long contentLength) {
            this.contentLength = contentLength;
            return this;
        }
    
        public IOUtils setEncodeType(String encodeType) {
            this.encodeType = encodeType;
            return this;
    
        }
    
        public static void close(Closeable closeable) {
            if (closeable != null) {
                try {
                    closeable.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public void stop() {
            this.isRunning = false;
        }
    
        public void read(boolean isLine, InputStream inputStream, IOListener ioListener) {
            if (isLine) {
                readLine2String(inputStream, ioListener);
            } else {
                read2String(inputStream, ioListener);
    
            }
        }
    
        /**
         * @param ioListener
         */
    
        public void read2String(String pathFile, IOListener ioListener) {
            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(pathFile);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
                return;
            }
            read2String(fileInputStream, ioListener);
        }
    
        public void read2String(InputStream inputStream, IOListener ioListener) {
    
            if (!(inputStream instanceof BufferedInputStream)) {
                inputStream = new BufferedInputStream(inputStream);
            }
            BufferedReader bufferedReader = null;
            InputStreamReader inputStreamReader = null;
            try {
                inputStreamReader = new InputStreamReader(inputStream, encodeType);
                bufferedReader = new BufferedReader(inputStreamReader);
    
                StringBuilder sb = new StringBuilder();
    
                char[] buf = new char[1024];
                int len = 0;
                long current = 0;
    
                while (isRunning && (len = bufferedReader.read(buf)) != -1) {
                    sb.append(buf, 0, len);
                    current += len;
                    ioListener.onLoading("", current, contentLength);
                }
    
                //中断
                if (len != -1) {
                    ioListener.onInterrupted();
                } else {
    
                    ioListener.onCompleted(sb.toString());
    
                }
    
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
            } catch (IOException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
    
            } finally {
                close(bufferedReader);
                close(inputStreamReader);
                close(inputStream);
            }
        }
    
        /**
         * 同步读取,共享锁,但无法同时进行写操作
         *
         * @param ioListener
         */
        public void read2StrSync(String pathFile, IOListener ioListener) {
            FileInputStream fileInputStream = null;
            FileChannel fileChannel = null;
            FileLock fileLock = null;//文件锁
    
            ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
            try {
    
                /**
                 * 读文件
                 */
    
                fileInputStream = new FileInputStream(pathFile);
                fileChannel = fileInputStream.getChannel();
                while (true) {
                    try {
                        fileLock = fileChannel.tryLock(0, Long.MAX_VALUE, true);//共享锁
                        break;
                    } catch (Exception e) {
                        System.out.println("有其他线程正在操作该文件,当前线程" + Thread.currentThread().getName());
                    }
                }
                if (fileLock != null) {
    
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    int len = 0;
                    long current = 0;
                    while (isRunning && (len = fileChannel.read(byteBuffer)) != -1) {
                        //0,byteBuffer.position(),必须写这个,否则GG,读取文件错乱
                        byteArrayOutputStream.write(byteBuffer.array(),0,byteBuffer.position());
                        current += len;
                        ioListener.onLoading("", current, fileChannel.size());
                        byteBuffer.clear();
                    }
                    if (fileLock != null && fileLock.isValid()) {
                    LogUtils.log("release-read-lock");
                        fileLock.release();
                    }
                    close(fileChannel);
                    close(fileInputStream);
                    //中断
                    if (len != -1) {
                        ioListener.onInterrupted();
                    } else {
                        ioListener.onCompleted(byteArrayOutputStream.toString("utf-8"));
    
                    }
                }
    
    
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
            } catch (IOException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
    
            } finally {
    
    
            }
        }
    
        /**
         * @param ioListener
         */
    
        public void readLine2String(String pathFile, IOListener ioListener) {
            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(pathFile);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
                return;
            }
            readLine2String(fileInputStream, ioListener);
        }
    
        /**
         * 一行一行地读
         *
         * @param inputStream
         * @param ioListener
         */
        public void readLine2String(InputStream inputStream, IOListener ioListener) {
    
            BufferedReader bufferedReader = null;
            InputStreamReader inputStreamReader = null;
            try {
                inputStreamReader = new InputStreamReader(inputStream, encodeType);
                bufferedReader = new BufferedReader(inputStreamReader);
    
                StringBuilder sb = new StringBuilder();
                long current = 0;
    
                String str;
                while (isRunning && (str = bufferedReader.readLine()) != null) {
                    sb.append(str);
                    current += str.length();
    
                    ioListener.onLoading(str, current, contentLength);
    
                }
    
                //中断
                if ((str = bufferedReader.readLine()) != null) {
                    ioListener.onInterrupted();
                } else {
    
                    ioListener.onCompleted(sb.toString());
    
                }
    
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
            } catch (IOException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
    
            } finally {
                close(bufferedReader);
                close(inputStreamReader);
                close(inputStream);
            }
        }
    
        public void readL2StrNoBuffer(String pathFile, IOListener ioListener) {
            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(pathFile);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
                return;
            }
            readL2StrNoBuffer(fileInputStream, ioListener);
        }
    
        /**
         * 一行一行地读,不拼接
         *
         * @param inputStream
         * @param ioListener
         */
        public void readL2StrNoBuffer(InputStream inputStream, IOListener ioListener) {
    
            BufferedReader bufferedReader = null;
            InputStreamReader inputStreamReader = null;
            try {
                inputStreamReader = new InputStreamReader(inputStream, encodeType);
                bufferedReader = new BufferedReader(inputStreamReader);
    
                long current = 0;
    
                String str;
                while (isRunning && (str = bufferedReader.readLine()) != null) {
                    current += str.length();
    
                    ioListener.onLoading(str, current, contentLength);
    
                }
    
                //中断
                if ((str = bufferedReader.readLine()) != null) {
                    ioListener.onInterrupted();
                } else {
    
                    ioListener.onCompleted("");
    
                }
    
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
            } catch (IOException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
    
            } finally {
                close(bufferedReader);
                close(inputStreamReader);
                close(inputStream);
            }
        }
    
        public void readL_N2String(String pathFile, IOListener ioListener) {
            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(pathFile);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
                return;
            }
            readL_N2String(fileInputStream, ioListener);
        }
    
        /**
         * 一行一行地读,\n拼接
         *
         * @param inputStream
         * @param ioListener
         */
        public void readL_N2String(InputStream inputStream, IOListener ioListener) {
    
            BufferedReader bufferedReader = null;
            InputStreamReader inputStreamReader = null;
            try {
                inputStreamReader = new InputStreamReader(inputStream, encodeType);
                bufferedReader = new BufferedReader(inputStreamReader);
    
                StringBuilder sb = new StringBuilder();
                long current = 0;
    
                String str;
                while (isRunning && (str = bufferedReader.readLine()) != null) {
                    sb.append(str);
                    sb.append("\n");
                    current += str.length();
    
                    ioListener.onLoading(str, current, contentLength);
    
                }
    
                //中断
                if ((str = bufferedReader.readLine()) != null) {
                    ioListener.onInterrupted();
                } else {
    
                    ioListener.onCompleted(sb.toString());
    
                }
    
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
            } catch (IOException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
    
            } finally {
                close(bufferedReader);
                close(inputStreamReader);
                close(inputStream);
            }
        }
    
        /**
         * 读取到文件
         *
         * @param inputStream
         * @param outputStream
         * @param ioListener
         */
        public void read2File(InputStream inputStream, OutputStream outputStream, IOListener ioListener) {
    
            try {
    
                byte[] buffer = new byte[1024];
                int len = 0;
                long current = 0;
    
                while (isRunning && (len = inputStream.read(buffer)) != -1) {
    
                    outputStream.write(buffer, 0, len);
                    current += len;
                    ioListener.onLoading(new String(buffer), current, contentLength);
                }
    
                outputStream.flush();
                //中断
                if (len != -1) {
                    ioListener.onInterrupted();
                } else {
    
                    ioListener.onCompleted(null);
    
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                close(outputStream);
                close(inputStream);
            }
        }
    
        /**
         * 将str写入文件
         */
        public void writeStr2File(String str, String pathFile, IOListener ioListener) {
            BufferedWriter bufferedWriter = null;
            OutputStreamWriter outputStreamWriter = null;
            OutputStream outputStream = null;
            try {
                outputStream = new FileOutputStream(pathFile);
                outputStreamWriter = new OutputStreamWriter(outputStream);
                bufferedWriter = new BufferedWriter(outputStreamWriter);
                bufferedWriter.write(str);
                ioListener.onCompleted("");
            } catch (IOException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
            } finally {
                close(bufferedWriter);
                close(outputStreamWriter);
                close(outputStream);
            }
        }
    
        /**
         * 将str写入文件,同步操作,独占锁
         */
        public void writeStr2ReplaceFileSync(String str, String pathFile, IOListener ioListener) {
            File file;
            try {
                file = FileUtils.createFile(pathFile);
            } catch (IOException e) {
                e.printStackTrace();
                ioListener.onFail("文件创建失败,请检查路径是否合法以及读写权限");
                return;
            }
            FileOutputStream fileOutputStream = null;
            FileChannel fileChannel = null;
            FileLock fileLock = null;//文件锁
            try {
    
                /**
                 * 写文件
                 */
                fileOutputStream = new FileOutputStream(file);
                fileChannel = fileOutputStream.getChannel();
                while (true) {
                    try {
                        fileLock = fileChannel.tryLock();//独占锁
                        break;
                    } catch (Exception e) {
                        System.out.println("有其他线程正在操作该文件,当前线程" + Thread.currentThread().getName());
                    }
                }
                if (fileLock != null) {
                    int len = 0;
                    long current = file.length();
                    if (isRunning ) {
                        fileChannel.write(ByteBuffer.wrap(str.getBytes()));
                        current += len;
                        LogUtils.log("当前线程" + Thread.currentThread().getName());
                        ioListener.onLoading(str.getBytes(), current, str.length());
                    }else {
                        //中断
                        ioListener.onInterrupted();
                    }
                    if (fileLock != null && fileLock.isValid()) {
                        LogUtils.log("release-write-lock");
                        fileLock.release();
                    }
                    close(fileChannel);
                    close(fileOutputStream);
                    if (file.length() == str.getBytes().length) {
                        ioListener.onCompleted(file);
                    }
                }
    
            } catch (IOException e) {
                e.printStackTrace();
                ioListener.onFail(e.getMessage());
            } finally {
            }
        }
    
      
    
    }
    
    

    写文件使用示例

     new IOUtils().writeStr2ReplaceFileSync(jsonObjectOld.toJSONString(), Constants.PATH_GAME_JSON, new IOListener() {
                                @Override
                                public void onCompleted(Object result) {
                                    
                                }
    
                                @Override
                                public void onLoading(Object readedPart, long current, long length) {
    
                                }
    
                                @Override
                                public void onInterrupted() {
    
                                }
    
                                @Override
                                public void onFail(String errorMsg) {
    
                                }
                            });
    

    读文件使用示例

    new IOUtils().read2StrSync(Constants.PATH_CONFIG_APPLICATION_JSON, new IOListener<String>() {
                @Override
                public void onCompleted(String result) {
                    
                }
    
                @Override
                public void onLoading(String readedPart, long current, long length) {
    
                }
    
                @Override
                public void onInterrupted() {
    
                }
    
                @Override
                public void onFail(String errorMsg) {
    
                }
            });
    

    各位老铁有问题欢迎及时联系、指正、批评、撕逼

    Github:
    https://github.com/AnJiaoDe
    简书:
    https://www.jianshu.com/u/b8159d455c69

    微信公众号


    这里写图片描述

    QQ群


    这里写图片描述

    相关文章

      网友评论

        本文标题:JAVA多线程读写文件如何做到线程安全?(文件锁,FileCha

        本文链接:https://www.haomeiwen.com/subject/xiacsctx.html