2012-02-17 3 views
4

У меня есть ByteBuffer в java, и вы хотите прочитать, затем условно изменить этот байт, например. с использованием метода, такого как:Атомное чтение, а затем запись части ByteBuffer в Java

public void updateByte(int index) { 
    byte b = this.buffer.getByte(index); 

    if (b == someByteValue) { 
     this.buffer.setByte(index, someNewByte); 
    } 
} 

Как я могу убедиться, что чтение, а затем изменение байта происходит атомарно?

Я не хочу, чтобы синхронизировать весь ByteBuffer или updateByte метода, так как я хочу несколько потоков, чтобы иметь возможность читать/писать различные байты буфера одновременно (т.е. updateByte можно назвать одновременно многими нитями, как длинный как index - другой).

ByteBuffer, который я использую, не поддерживается байтом [], поэтому bb.hasArray() == false в приведенном выше примере.

ответ

3

Короткий ответ: вы не можете, не прибегая в JNI.

Более длинный ответ: в ByteBuffer API нет обновлений атома. Более того, взаимодействие ByteBuffer с памятью не строго определено. И в реализации Sun методы, используемые для доступа к необработанной памяти, не пытаются очистить кеш, поэтому вы можете увидеть устаревшие результаты на многоядерном процессоре.

Кроме того, имейте в виду, что Buffer (и его подклассы, такие как ByteBuffer) явно задокументированы как небезопасные потоки. Если у вас есть несколько потоков, обращающихся к одному и тому же буфере, вы (1) полагаетесь на поведение реализации для абсолютного доступа или (2) записываете неработающий код для относительного доступа.

+0

Это правда, я буду использовать MappedByteBuffer, созданный из FileChannel (который является потокобезопасным), поэтому я предположил, что отображаемый буфер будет безопасно тоже. –

+1

@Dave - MappedByteBuffer не имеет никакого отношения к FileChannel, используемому для его создания. Последний просто используется для предоставления аргументов системному вызову * mmap *. – kdgregory

+0

Вы можете создать и использовать свои собственные экземпляры java.util.concurrent.lock.ReadWriteLock. Все коды, которые читают/записывают в конкретный «атомный байт» или «атомный байтовый регион», сначала вынимают соответствующую блокировку для этого байта/региона. :) –

3

Я не верю, что вы можете получить доступ к байту атомарно в Java. Лучшее, что вы можете сделать, это изменить значения int. Это позволит вам смоделировать изменение одного байта.

Вы можете использовать UnSafe (на многих виртуальных машинах), чтобы сделать сравнение и обмен для массива() (куча ByteBuffer) или адрес() (прямой ByteBuffer)

5

Как получить набор явных объектов блокировки для частей ByteBuffer (порты могут быть очень маленькими, например, одним словом или достаточно большими, например, четыре четверть буфера)?

Когда поток хочет проверить и изменить байт, он должен сначала получить блокировку для соответствующей части, выполнить ее работу, а затем отпустить блокировку.

Это позволит получить доступ к различным частям данных несколькими потоками, не требуя глобальной синхронизации.

1

Лично я бы заблокировал мьютекс, пока не выясню смещение, чтобы записать данные, а затем отпустить мьютекс. Таким образом, вы блокируете в течение очень малого времени

0

Должно быть возможно заблокировать ByteBuffer. Методы:

  • Вы можете создать список объектов блокировки и заблокировать только одну область за чтение байтового буфера. Как предполагает ДНК, это должно быть самым быстрым решением.
  • или вы можете использовать memory mapping to solve this, а затем использовать FileChannel.lock, который также блокирует область байтового буфера, но на более низком уровне. Редактировать: это только защищает доступ от внешних программ. IMO
  • Или вы можете использовать несколько небольших, но синхронизированных данных ByteBuffers +.Это interesting to note, что нити должна видеть изменения сразу друг от друга (это где я получил идею MMAP)
1

Долгой длинную нить о параллельной DirectByteBuffer в этом list:

Ответ должен быть " ДА".

Еще один пример BIG - NIO.2. операция записи/чтения представляет байт-буферы, это нормально при вызове CompletionHandler.

Из-за того, что в случае NIO.2 применяется только приложение DirectByteBuffer. Для Non-Direct-ByteBuffer, который «клонирован» в DirectByteBuffer, это НЕ параметры реальных операций нижнего уровня.

0

Я думаю, что создание критического раздела кода под контролем блокировки должно быть чистым решением. Однако не используйте синхронизацию напрямую, если ваш прецедент имеет большое количество чтений по сравнению с записью. Я предлагаю вам использовать ReentrantReadWriteLock как часть вашего решения. В функции, где вы изменяете ByteBuffer, вы берете writeLock(). Lock(), а затем ваш код. И во время чтения используйте readLock(). Lock(). Вы можете прочитать больше о блокировке чтения и записи по указанной ссылке. В основном это позволит одновременное чтение, но не одновременную запись, а во время записи происходит чтение потоков.

Смежные вопросы