0

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

Сохраняя модель памяти Java в памяти, как я могу обеспечить, чтобы данные, переданные в потребительский поток, имели полную «видимость»?

Я знаю, что в java.util.concurrent есть структуры данных, такие как ConcurrentLinkedQueue, которые созданы специально для этого, но я хочу сделать это как можно более низким, не используя их, и иметь полную прозрачность в отношении того, что происходит под для обеспечения части видимости памяти.

+0

* «полная прозрачность того, что происходит под обложками, чтобы обеспечить часть видимости памяти». * - Не доверяете ли вы 'ConcurrentLinkedQueue'? –

+1

в качестве примечания, ConcurrentLinkedQueue не очень подходит для производителя/потребителя, поскольку он не имеет блокирующих устройств.как правило, реализация BlockingQueue идеально подходит для производителя/потребителя. – jtahlborn

ответ

1

Если вы хотите «низкий уровень», посмотрите на volatile и synchronized.

1

Для передачи данных вам необходимо поле, доступное для всех потоков. В вашем случае это действительно должна быть какая-то коллекция для обработки нескольких записей. Если вы сделали поле final, ссылаясь, скажем, на ConcurrentLinkedQueue, вы бы в значительной степени сделали. Поле могло быть обнародовано, и все могли его увидеть, или вы могли бы сделать его доступным с помощью геттера.

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

if (!queue.isEmpty()) obj = queue.remove(); 

Если все это не синхронизированы, queue вполне может сказать вам, что это не пусто, то бросание NoSuchElementException, когда вы пытаетесь получить следующий элемент. (Интерфейс ConcurrentLinkedQueue специально разработан, чтобы позволить вам выполнять такие операции с помощью одного вызова метода. Посмотрите на него, даже если вы не хотите его использовать.)

Простым решением является обернуть очередь в другую объект, методы которого тщательно выбраны. и все синхронизированы. Обернутый класс, даже если это LinkedList или ArrayList, теперь будет действовать (если вы сделаете это правильно), как CLQ, и он может быть свободно выпущен для остальной части программы.

Таким образом, вы бы, что действительно глобальное поле с неизменяемой (final) ссылки на класс-оболочку, которая содержит LinkedList (например) и синхронизированной методы, которые используют LinkedList для хранения и доступа к данным. Класс оболочки, например CLQ, будет потокобезопасным.

Некоторые варианты могут быть желательными. Возможно, имеет смысл объединить оболочку с другим классом высокого уровня в вашей программе. Также может иметь смысл создавать и делать доступными экземпляры вложенных классов: возможно, один, который добавляет только в очередь и тот, который удаляет только из него. (Вы не смогли сделать это с помощью CLQ.)

Последнее замечание: синхронизировав все, следующий шаг - выяснить, как несинхронизировать (чтобы потоки не слишком много ожидали), не нарушая безопасности потоков. Очень хорошо работайте над этим, и вы закончите переписывание ConcurrentLinkedQueue.