Немного вопросов о многопоточности

194
24 марта 2018, 16:17

Сам не первый год пишу на java, но лишь в рамках хобби, с многопоточностью приходится не так часто работать.

Заинтересовали несколько вопросов, а имённо:

  1. Допустим, мне нужно писать и вычитывать примитивный тип на каком-то объекте (из разных потоков). Допустим, поля открытые (public).

    Если верить справке java, операции чтения и записи ссылок являются атомарными (логично, ведь размер такой ссылки обычно помещается в регистр процессора, да и система его поддерживает). Тогда чтение и запись поля, хранящего ссылку на объект, вполне себе нормальная операция для выполнения в разных потоках.

    Также справка java уверяет, что чтение и запись примитивных типов, исключая long и double, тоже являются атомарными. Но как быть с этими двумя? Как я понимаю, их нужно лишь пометить как volatile. Но есть ли какие-то побочные эффекты, или этот модификатор только и делает, что гарантирует атомарность операций над переменной?

    Хорошо. А теперь, к примеру, я хочу читать и писать поля через геттеры-сеттеры. Если мои геттеры-сеттеры не меняют состояние каких-либо внешних данных, а лишь читают и пишут переменную, достаточно ли мне пометить такую переменную как volatile, или же нужно отмечать геттер/сеттер как syncronized? (При условии, что доступ к переменным возможен лишь через геттер и сеттер).

  2. Работа с объектами. А конкретно, с объектами, что не меняют состояния.

    К примеру, String. Насколько мне известно, данный тип никогда не меняет состояния, а все его методы, возвращающие строку, возвращают новый экземпляр.

    Так вот, могу ли я обращаться к такому объекту из разных потоков без дополнительной синхронизации? Или же я могу тогда что-то упустить (вопрос для случая, когда я могу лишь получить объект, но не изменить значение поля, на него ссылающегося).

Благодарю за терпение того, кто сможет на эти вопросы ответить)

Answer 1
  1. Важно, что volatile гарантирует видимость. Изменения volatile-поля в одном потоке будут сразу видны в другом. Если вам нужно потокобезопасно выполнить более сложную процедуру, чем присвоение - воспользуйтесь synchronized или j.u.c.Atomic*-типами, чтобы избежать возникновения гонок (race condition).

    Пример: любой многопоточный счетчик

    // Плохо 
    private volatile long counter;
    public long getId() { 
        counter += 1;
        return counter;
    }
    // Хорошо
    private volatile AtomicLong counter = new AtomicLong(0);
    public long getId() { 
        return counter.incrementAndGet();
    }
    // Тоже хорошо
    private volatile long counter;
    public synchronized long getId() { 
        counter += 1;
        return counter;
    }
    

    Помните, что synchronized - это всегда явная блокировка (но она довольно дёшева при низкой конкуренции), а Atomic-примитивы используют бесконечный цикл с CAS-инструкциями и могут хорошо оптимизироваться.

  1. Объекты с неизменяемым состоянием (например, все поля такого объекта final) назвают immutable объектами, и с ними безопасно работать из разных потоков. Но если внутреннее состояние объекта содержит изменяемый какой-то компонент и он как-то может быть доступен снаружи - придется дополнительно думать о потокобезопасности. Пример:

    // потокобезопасный класс 
    public final class SafeFoo { 
        private final Date date = new Date();
    }
    
    // не потокобезопасный класс 
    public final class UnsafeFoo { 
        private final Date date = new Date();
        public Date getDate() { return date; }
    }
    

    Первый класс полностью потокобезопасен. Второй - нет. Метод getDate() осуществляет публикацию внутреннего состояния внешнему коду (unsafe publication). И где-то снаружи можно сделать так, изменив внутренне состояние:

    unsafeFoo.getDate().setHours(0);
    

    Защититься от этого можно, например созданием копии:

    // потокобезопасный класс 
    public final class SafeAgainFoo { 
        private final Date date = new Date();
        public Date getDate() { return date.clone(); }
    }
    
READ ALSO
Поиск по бд Firebase | Java-android

Поиск по бд Firebase | Java-android

Есть ListView с множественным выбором,в 1 элемент которого входит к примеру - ингридиент для рецептовКак мне реализовать поиск по моей бд в firebase...

137
Ehcache возвращает null

Ehcache возвращает null

element = new Element(StringId, cacheput(element);

154
Дата последнего входа от User в бд Firebase.

Дата последнего входа от User в бд Firebase.

Я хочу получить последнюю дату входа пользователя в базу данных Firebase для AndroidИнформация нужна для удаления неактивных пользователей из базы...

176