Передача изменяемых данных между потоками

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

Однако в моем случае двум потокам не требуется изменять структуру данных в одно и то же время; скорее, поток A, который обычно владеет структурой данных, должен временно передать последний потоку B, а поток B должен передать структуру данных обратно в поток A после выполнения некоторых длительных изменений на нем.

Безопасно ли это передавать эту изменчивую структуру данных между потоками, если она гарантирует, что потоки не изменяют данные одновременно?

Ответы

Ответ 1

Если вы можете гарантировать, что потоки не будут изменять дерево одновременно (то есть атомарно передавая единственную ссылку на дерево), это отлично с точки зрения безопасности потока.

Видимость/согласованность данных - еще одна проблема. Если все поля в дереве (рекурсивно) объявлены volatile, изменения, сделанные одним потоком, могут не стать видимыми для другого потока. Чтобы избежать этого, убедитесь, что монитор (который действует как барьер памяти и гарантирует, что все записи становятся видимыми) приобретается, когда потоки обмениваются собственностью дерева.

Ответ 2

Да, то, что вы описываете, будет работать только отлично , если вы предпримете определенные шаги, чтобы избежать ошибок согласованности памяти, когда вы передаете объект между потоками. Использование блокировки - один из способов достижения этого, но есть и другие - менее дорогие способы.

tutorial является хорошей отправной точкой.

В принципе, вам нужно убедиться, что когда поток A передает объект в поток B, все изменения A происходят до того, как B обращается к объекту.

Там больше в JLS, но это довольно технический.

Ответ 3

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

В принципе, нельзя быть уверенным в постоянной видимости между потоками без правильной синхронизации - например, synchronized или другой - перед гарантиями JLS. То есть, несмотря на то, что не может быть "одновременных изменений", нет никакой гарантии, что поток, не связанный с писателем, видит изменения в указанном объекте.

Ответ 4

Да, но это все еще задает вопрос о том, как вы передадите права собственности на данные между потоками. Ответ заключается в том, что это обычно делается с помощью блокировок; удерживание блокировки означает, что данный поток в настоящее время является ответственным, которому разрешено мутировать объект.

Также важно отметить, что безопасность потоков - это проблема, когда есть один или несколько читателей и, по крайней мере, один писатель. Просто потому, что оба объекта не изменяют объект одновременно, это все равно будет проблемой, если один поток будет считывать из структуры в то же время, что он мутирован.

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

Ответ 5

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

Блокировка делает две вещи.

  • Это предотвращает одновременный доступ двух потоков к одним и тем же данным.
  • Когда происходит блокировка всех кешей, которые программа/система сброшена. Именно поэтому ключевое слово в java называется synchronized.

Если каким-то образом вы можете создать механизм, в котором он имеет блокировку (это действительно то, что вы говорите), вам все равно нужно убедиться, что все данные синхронизированы по потокам. Блокировка - это не единственный способ сделать это, но его добавленная особенность.

Ответ 6

Я предполагаю, что вы создали пользовательскую структуру данных.

Попробуйте создать класс dataStructure Synchronized как это Используйте осторожно с блоком Synchronized, где это необходимо, как показано в примере.

Если вам нужно обмениваться одними и теми же данными по всему потоку, создайте методы static Synchronized и private static, поэтому, хотя по ошибке вы создаете несколько экземпляров класса dataStructure, это потокобезопасно

Ответ 7

Это можно сделать с помощью простой изменчивой вспомогательной переменной:

class OwnedTree{
    private volatile Thread owner;
    private Tree tree;

    //once a thread calls this with a the!=Thread.currentThread() then it may not use Tree returned from getTree any more
    public void passToThread(Thread thr){
        if(Thread.currentThread().equals(owner))
            owner = thr;//volatile write ensures happens-before
    }

    public Tree getTree(){
        if(!Thread.currentThread().equals(owner))//volatile read ensures happens-before
            throw new IllegalStateException();//or return null;
        return tree;
    }

    public Thread getOwner(){
        return owner;
    }

}

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