java Memory mapped файлы многопоточность чтение / запись
У меня есть 2 потока, которые одновременно обращаются к одному и тому же большому файлу (.txt).
1-й поток читает из файла. 2-й поток пишет в файл.
Оба потока обращаются к одному и тому же блоку, например (начало:0, размер блока:10), но с разными экземплярами канала и буфера
Читатель:
{
int BLOCK_SIZE = 10;
byte[] bytesArr = new byte[BLOCK_SIZE];
File file = new File("/db.txt");
RandomAccessFile randomFile = new RandomAccessFile(file, "r");
FileChannel channel = randomFile.getChannel();
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_ONLY, 0, BLOCK_SIZE);
map.get(bytesArr , 0, BLOCK_SIZE);
channel.close();
}
Автор:
{
int BLOCK_SIZE = 10;
File file = new File("/db.txt");
RandomAccessFile randomFile = new RandomAccessFile(file, "rw");
FileChannel channel = randomFile.getChannel();
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, BLOCK_SIZE);
map.put(bytesToWrite);
channel.close();
}
Я знаю, что если оба запускаются одновременно, я получу перекрывающиеся исключения! НО что я хотел бы знать, в какой момент происходит перекрытие? Я имею в виду, когда происходит "замок" точно? Пример: допустим, сначала писатель получает доступ, затем, если читатель попытается получить доступ, в какой момент это возможно?:
FileChannel channel = randomFile.getChannel();
// 1- can reader access here?
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, BLOCK_SIZE);
// 2- can reader access here?
map.put(bytesToWrite);
// 3- can reader access here?
channel.close();
// 4- can reader access here?
1, 2, 3 или 4?
Нет 4 уверен, потому что канал был закрыт! А как насчет других моментов?
Спасибо!
2 ответа
Я подвожу несколько заметок из чата с ОП. У OP была ментальная модель (как и у большинства из нас), что, как только поток записывает в структуру данных, эта структура данных сразу видна всем другим потокам. В тестах OPs с использованием отображенных в память файлов он подтвердил, что это похоже на один процессор In tel с одним сокетом.
К сожалению, это не так и является областью, в которой Java может и действительно показывает поведение оборудования. Java была разработана, чтобы предполагать, что код является однопоточным, и, таким образом, может быть оптимизирован как таковой до тех пор, пока не будет сказано иначе. Что это означает, будет отличаться в зависимости от аппаратного обеспечения и версии точки доступа (и статистики, собранной точкой доступа). Эта сложность и работающий на одном сокете процессор In tel аннулировали тест OPs.
Для получения дополнительной информации следующие ссылки помогут глубже понять "модель памяти Java". И, в частности, синхронизация означает не просто "взаимное исключение"; в аппаратном плане речь идет также о "видимости данных" и "порядке команд". Две темы, которые однопоточный код принимают как должное.
Не беспокойтесь, если на это потребуется время, и вы сначала чувствуете себя подавленным. Сначала мы все так чувствовали. Java делает удивительную работу, скрывая эту сложность, если и только если вы следуете этому одному простому правилу. Когда поток читает или изменяет общую структуру данных, он должен находиться в синхронизированном блоке. То есть как поток записи, так и поток чтения. Очевидно, я упрощаю, но следую этому правилу, и программа всегда будет работать. Разрушайте его, только если у вас есть очень глубокое понимание модели памяти Java, барьеров памяти и того, как она связана с различным аппаратным обеспечением (и даже тогда эксперты по параллелизму даже избегают нарушать это правило, если они могут; переход на однопотоковый режим часто намного проще и может быть на удивление быстрым.. по этой причине многие системы с низкой задержкой предназначены для использования в основном однопоточными).
Чтобы напрямую ответить на вопрос ОП. Пример кода из вопроса не имеет блокировок. Нет барьеров памяти, нет контроля параллелизма вообще. Таким образом, поведение взаимодействия чтения и записи не определено. Они могут работать, они не могут. Они могут работать большую часть времени. In tel имеет самые сильные гарантии памяти для всех процессоров, и выполнение тестовых случаев на одном сокете Процессор In tel пропустит много сложных ошибок. Sun была поймана этим и до выхода Java 5 и JSR 133 (подробнее читайте в статье о том, почему в Java была нарушена двойная проверка блокировки).
Вы не получите никаких исключений блокировки из этого кода или каких-либо блоков. Блокировки файлов работают между процессами, а не между потоками. Здесь вам нужна синхронизация, или семафоры, или ReadWriteLocks. И нет необходимости использовать два канала.