Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
当多个线程试图在JAVA中使用NIO追加内容时,是否需要锁定文件?_Java_Multithreading_Java 8_Java.util.concurrent_Threadpoolexecutor - Fatal编程技术网

当多个线程试图在JAVA中使用NIO追加内容时,是否需要锁定文件?

当多个线程试图在JAVA中使用NIO追加内容时,是否需要锁定文件?,java,multithreading,java-8,java.util.concurrent,threadpoolexecutor,Java,Multithreading,Java 8,Java.util.concurrent,Threadpoolexecutor,首先我创建了一个空文件,然后我调用了一些线程来搜索数据库并获取结果内容,然后附加到文件中。结果内容为字符串类型,可能为20M。每个线程应该一次写入一个文件。我已经测试了很多次,我发现没有必要锁定。是这样吗?该示例的总行数为1000。何时需要添加写锁才能对文件进行操作 String currentName = "test.txt"; final String LINE_SEPARATOR = System.getProperty("line.separator"); Thr

首先我创建了一个空文件,然后我调用了一些线程来搜索数据库并获取结果内容,然后附加到文件中。结果内容为字符串类型,可能为20M。每个线程应该一次写入一个文件。我已经测试了很多次,我发现没有必要锁定。是这样吗?该示例的总行数为1000。何时需要添加写锁才能对文件进行操作

    String currentName = "test.txt";
    final String LINE_SEPARATOR = System.getProperty("line.separator");
    ThreadPoolExecutor pool = new ThreadPoolExecutor(
            10, 100, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
    for (int i = 0; i < 500; i++) {
        pool.execute(() -> {
            try {
                appendFileByFilesWrite(currentName, "abc" +
                        ThreadLocalRandom.current().nextInt(1000) + LINE_SEPARATOR);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    IntStream.range(0, 500).<Runnable>mapToObj(a -> () -> {
        try {
            appendFileByFilesWrite( currentName,
                    "def" + ThreadLocalRandom.current().nextInt(1000) +
                    LINE_SEPARATOR);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }).forEach(pool::execute);

    pool.shutdown(); 
答案是:永远

你的测试对你有用。马上今天也许在满月的时候,它不会。也许如果你买了一台新电脑,或者你的操作系统供应商更新了,或者JDK更新了,或者你正在winamp中播放一首布兰妮·斯皮尔斯的歌,那就不会了

规范说,在多个步骤中抹去写操作是合法的,并且SOO.APPEND的行为在这一点上是未定义的。如果同时编写“Hello”和“World”,则文件可能最终包含“helworlld”。可能不会。但它可以

一般来说,并发中的bug很难测试,有时甚至不可能测试。不会让它成为一个小虫子;大多数情况下,你会得到大量的bug报告,并且你会回答“无法复制”所有的bug报告。这不是个好地方

如果您想观察实际问题,最有可能的是,您应该在writer中编写非常长的字符串;其目的是最终得到实际的低级磁盘命令,该命令涉及多个分离出的块。即使这样,也不能保证您会观察到问题。然而,缺乏证据并不是缺乏证据。

答案是:永远

你的测试对你有用。马上今天也许在满月的时候,它不会。也许如果你买了一台新电脑,或者你的操作系统供应商更新了,或者JDK更新了,或者你正在winamp中播放一首布兰妮·斯皮尔斯的歌,那就不会了

规范说,在多个步骤中抹去写操作是合法的,并且SOO.APPEND的行为在这一点上是未定义的。如果同时编写“Hello”和“World”,则文件可能最终包含“helworlld”。可能不会。但它可以

一般来说,并发中的bug很难测试,有时甚至不可能测试。不会让它成为一个小虫子;大多数情况下,你会得到大量的bug报告,并且你会回答“无法复制”所有的bug报告。这不是个好地方


如果您想观察实际问题,最有可能的是,您应该在writer中编写非常长的字符串;其目的是最终得到实际的低级磁盘命令,该命令涉及多个分离出的块。即使这样,也不能保证您会观察到问题。然而,缺少证据并不是缺少证据。

您可以使用fileLock,也可以将synchronized添加到方法中

while (true) {
  try {
  lock = fc.lock();
  break;
   } catch (OverlappingFileLockException e) {
    Thread.sleep(1 * 1000);
   }
 }
appendFileByFilesWrite( fileName, fileContent) ;
或者像这样改变:

public synchronized  static void appendFileByFilesWrite(String fileName,String fileContent) throws IOException {
    Files.write(Paths.get(fileName), fileContent.getBytes(),StandardOpenOption.APPEND);
}

您可以使用fileLock,也可以将synchronized添加到方法中

while (true) {
  try {
  lock = fc.lock();
  break;
   } catch (OverlappingFileLockException e) {
    Thread.sleep(1 * 1000);
   }
 }
appendFileByFilesWrite( fileName, fileContent) ;
或者像这样改变:

public synchronized  static void appendFileByFilesWrite(String fileName,String fileContent) throws IOException {
    Files.write(Paths.get(fileName), fileContent.getBytes(),StandardOpenOption.APPEND);
}

当我需要锁定文件时,我使用这个类。它允许跨多个JVM和多个线程进行读写锁定

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import com.lfp.joe.core.process.CentralExecutor;

public class FileLocks {

    private static final String WRITE_MODE = "rws";
    private static final String READ_MODE = "r";
    private static final Map<String, LockContext> JVM_LOCK_MAP = new ConcurrentHashMap<>();

    private FileLocks() {
    }

    public static <X> X read(File file, ReadAccessor<X> accessor) throws IOException {
        return access(file, false, fc -> {
            try (var is = Channels.newInputStream(fc);) {
                return accessor.read(fc, is);
            }
        });
    }

    public static void write(File file, WriterAccessor accessor) throws IOException {
        access(file, true, fc -> {
            try (var os = Channels.newOutputStream(fc);) {
                accessor.write(fc, os);
            }
            return null;
        });
    }

    public static <X> X access(File file, boolean write, FileChannelAccessor<X> accessor)
            throws FileNotFoundException, IOException {
        Objects.requireNonNull(file);
        Objects.requireNonNull(accessor);
        String path = file.getAbsolutePath();
        var lockContext = JVM_LOCK_MAP.compute(path, (k, v) -> {
            if (v == null)
                v = new LockContext();
            v.incrementAndGetThreadCount();
            return v;
        });
        var jvmLock = write ? lockContext.getAndLockWrite() : lockContext.getAndLockRead();
        try (var randomAccessFile = new RandomAccessFile(file, write ? WRITE_MODE : READ_MODE);
                var fileChannel = randomAccessFile.getChannel();) {
            var fileLock = write ? fileChannel.lock() : null;
            try {
                return accessor.access(fileChannel);
            } finally {
                if (fileLock != null && fileLock.isValid())
                    fileLock.close();
            }
        } finally {
            jvmLock.unlock();
            JVM_LOCK_MAP.compute(path, (k, v) -> {
                if (v == null)
                    return null;
                var threadCount = v.decrementAndGetThreadCount();
                if (threadCount <= 0)
                    return null;
                return v;
            });
        }
    }

    public static interface FileChannelAccessor<X> {

        X access(FileChannel fileChannel) throws IOException;

    }

    public static interface ReadAccessor<X> {

        X read(FileChannel fileChannel, InputStream inputStream) throws IOException;

    }

    public static interface WriterAccessor {

        void write(FileChannel fileChannel, OutputStream outputStream) throws IOException;

    }

    private static class LockContext {

        private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
        private long threadCount = 0;

        public long incrementAndGetThreadCount() {
            threadCount++;
            return threadCount;
        }

        public long decrementAndGetThreadCount() {
            threadCount--;
            return threadCount;
        }

        public Lock getAndLockWrite() {
            var lock = rwLock.writeLock();
            lock.lock();
            return lock;
        }

        public Lock getAndLockRead() {
            var lock = rwLock.readLock();
            lock.lock();
            return lock;
        }
    }
}
并阅读:

File file = new File("test/lock-test.txt")
var lines = FileLocks.read(file, (fileChannel, inputStream) -> {
    try (var br = new BufferedReader(new InputStreamReader(inputStream));) {
        return br.lines().collect(Collectors.toList());
    }
});

当我需要锁定文件时,我使用这个类。它允许跨多个JVM和多个线程进行读写锁定

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import com.lfp.joe.core.process.CentralExecutor;

public class FileLocks {

    private static final String WRITE_MODE = "rws";
    private static final String READ_MODE = "r";
    private static final Map<String, LockContext> JVM_LOCK_MAP = new ConcurrentHashMap<>();

    private FileLocks() {
    }

    public static <X> X read(File file, ReadAccessor<X> accessor) throws IOException {
        return access(file, false, fc -> {
            try (var is = Channels.newInputStream(fc);) {
                return accessor.read(fc, is);
            }
        });
    }

    public static void write(File file, WriterAccessor accessor) throws IOException {
        access(file, true, fc -> {
            try (var os = Channels.newOutputStream(fc);) {
                accessor.write(fc, os);
            }
            return null;
        });
    }

    public static <X> X access(File file, boolean write, FileChannelAccessor<X> accessor)
            throws FileNotFoundException, IOException {
        Objects.requireNonNull(file);
        Objects.requireNonNull(accessor);
        String path = file.getAbsolutePath();
        var lockContext = JVM_LOCK_MAP.compute(path, (k, v) -> {
            if (v == null)
                v = new LockContext();
            v.incrementAndGetThreadCount();
            return v;
        });
        var jvmLock = write ? lockContext.getAndLockWrite() : lockContext.getAndLockRead();
        try (var randomAccessFile = new RandomAccessFile(file, write ? WRITE_MODE : READ_MODE);
                var fileChannel = randomAccessFile.getChannel();) {
            var fileLock = write ? fileChannel.lock() : null;
            try {
                return accessor.access(fileChannel);
            } finally {
                if (fileLock != null && fileLock.isValid())
                    fileLock.close();
            }
        } finally {
            jvmLock.unlock();
            JVM_LOCK_MAP.compute(path, (k, v) -> {
                if (v == null)
                    return null;
                var threadCount = v.decrementAndGetThreadCount();
                if (threadCount <= 0)
                    return null;
                return v;
            });
        }
    }

    public static interface FileChannelAccessor<X> {

        X access(FileChannel fileChannel) throws IOException;

    }

    public static interface ReadAccessor<X> {

        X read(FileChannel fileChannel, InputStream inputStream) throws IOException;

    }

    public static interface WriterAccessor {

        void write(FileChannel fileChannel, OutputStream outputStream) throws IOException;

    }

    private static class LockContext {

        private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
        private long threadCount = 0;

        public long incrementAndGetThreadCount() {
            threadCount++;
            return threadCount;
        }

        public long decrementAndGetThreadCount() {
            threadCount--;
            return threadCount;
        }

        public Lock getAndLockWrite() {
            var lock = rwLock.writeLock();
            lock.lock();
            return lock;
        }

        public Lock getAndLockRead() {
            var lock = rwLock.readLock();
            lock.lock();
            return lock;
        }
    }
}
并阅读:

File file = new File("test/lock-test.txt")
var lines = FileLocks.read(file, (fileChannel, inputStream) -> {
    try (var br = new BufferedReader(new InputStreamReader(inputStream));) {
        return br.lines().collect(Collectors.toList());
    }
});

基本上答案:基本上答案:…而且使用单个编写器会更有效。如何使用单个编写器?我的方法是静态的。你能给我一个例子吗@Holger@flower当您预先创建FileWriter时,AppendFileByFileWrite方法将简化为一个简单的writer.writestring,换句话说,您根本不需要静态方法。@Holger,我将其作为工具类编写,因此当我在另一个类中使用它时,我不需要创建实例。@flower工具本身应该是有用的,而不是目的。创建一个工具,使简单的call writer.writestring变得更加复杂,同时产生您没有遇到的问题,这是毫无意义的。我不知道您为什么如此痴迷于“需要创建实例”,尤其是对于I/O操作,但当然,您的工具方法创建的对象比在整个操作过程中保留编写器的方法多。它从fileContent.getBytes的结果开始,然后是Files.write…并没有神奇的效果。它会在每次调用时创建一个OuputStream…而且使用一个编写器会更有效。如何使用一个编写器?我的方法是静态的。你能给我一个例子吗@Holger@flower当您预先创建FileWriter时,AppendFileByFileWrite方法将简化为一个简单的writer.writestring,换句话说,您根本不需要静态方法。@Holger,我将其作为工具类编写,因此当我在另一个类中使用它时,我不需要创建实例。@flower工具本身应该是有用的,而不是目的。创建一个工具,使简单的call writer.writestring变得更加复杂,同时产生您没有遇到的问题 没有,就没有意义。我不知道您为什么如此痴迷于“需要创建实例”,尤其是对于I/O操作,但当然,您的工具方法创建的对象比在整个操作过程中保留编写器的方法多。它从fileContent.getBytes的结果开始,然后是Files.write…并没有神奇的效果。它在每次调用时创建一个输出流。