Ответ
Потокобезопасный доступ к файлам в Java требует синхронизации операций записи и, в некоторых случаях, чтения. Встроенный монитор (synchronized) — базовый, но не всегда оптимальный механизм.
Способы синхронизации доступа к файлу:
-
Использование
synchronized(Intrinsic Lock):public class LogWriter { private final Object lock = new Object(); public void writeEntry(String entry) { synchronized(lock) { // Только один поток пишет в файл try (FileWriter fw = new FileWriter("app.log", true)) { fw.write(entry + "n"); } catch (IOException e) { /* обработка ошибки */ } } } } -
Использование
ReentrantReadWriteLock(для "много читателей, один писатель"):import java.util.concurrent.locks.ReentrantReadWriteLock; public class ThreadSafeFileCache { private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); public String readFromFile() { rwLock.readLock().lock(); // Много потоков могут читать одновременно try { // ... чтение файла } finally { rwLock.readLock().unlock(); } } public void writeToFile(String data) { rwLock.writeLock().lock(); // Только один поток может писать try { // ... запись в файл } finally { rwLock.writeLock().unlock(); } } } -
Использование файловых блокировок ОС (
java.nio.channels.FileLock):import java.nio.channels.FileChannel; import java.nio.file.StandardOpenOption; public class NioFileLockExample { public void writeWithLock(String data) throws IOException { try (FileChannel channel = FileChannel.open( Paths.get("data.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE)) { // Эксклюзивная блокировка файла (для записи) try (FileLock lock = channel.lock()) { // Безопасная запись в файл channel.write(ByteBuffer.wrap(data.getBytes())); } // Lock автоматически освобождается } } }
Рекомендации:
- Для простых сценариев достаточно
synchronized. - Если чтение происходит часто, а запись редко, используйте
ReentrantReadWriteLockдля повышения производительности. FileLockполезен для координации между разными процессами (JVM), а не только потоками.- Рассмотрите возможность отказа от разделяемого файла — используйте очередь задач и один поток-писатель.