Является ли строка get/set потокобезопасной?

Допустим, у меня есть следующее,

public class Foo{
    private String bar;

    public String getBar(){
        return bar;
    }

    public void setBar(String bar){
        this.bar = bar;
    }
}

Являются ли эти методы автоматически потокобезопасными из-за неизменной природы класса String или требуется какой-то механизм блокировки?


person mre    schedule 25.02.2013    source источник
comment
Я предполагаю, что вы имеете в виду String в установщике.   -  person Theodoros Chatzigiannakis    schedule 25.02.2013
comment
@TheodorosChatzigiannakis, да, моя ошибка! Фиксированный.   -  person mre    schedule 25.02.2013
comment
Связанный с # вопрос: stackoverflow.com/questions/ 3595114/   -  person PermGenError    schedule 25.02.2013
comment
Объект String есть, неконечная ссылка на него — нет. Дополнительные сведения см. на странице stackoverflow.com/a/6060405/1812922.   -  person alldayremix    schedule 25.02.2013


Ответы (4)


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

  • Сделайте bar volatile или
  • Сделайте методы synchronized или
  • Используйте AtomicReference<String>.

Чтение и запись bar сами по себе являются атомарными, но атомарность не является потокобезопасностью.

http://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html


Для более подробного ознакомления с параллелизмом в Java скачайте копию Java Параллелизм на практике (он же JCIP).

person Matt Ball    schedule 25.02.2013
comment
+1 Или просто объявите bar как final, чтобы ссылку нельзя было переназначить другому значению. Конечно, никакого сеттера тогда не будет :) - person Eng.Fouad; 25.02.2013
comment
Это неизменно. Нет необходимости в сеттере в этом случае. И вы должны обязательно предоставить конструктор для инициализации значения, потому что вы не можете изменить его после выхода из конструктора в этом случае. - person duffymo; 25.02.2013
comment
... Из любопытства, действительно ли что-то из этого имеет значение? Я имею в виду, что если ваша реальная проблема связана с состоянием гонки, это все еще может произойти, несмотря на volatile или synchronized — это просто уменьшает «окно», в котором это может произойти, верно? Или я что-то здесь упускаю? - person Clockwork-Muse; 25.02.2013
comment
@Clockwork-Muse, а что это за состояние гонки? TBH, подобные вопросы объясняют, почему я избегаю совместного использования изменяемых (не параллельных библиотек) объектов между потоками, насколько это возможно. - person Matt Ball; 25.02.2013
comment
Первый поток постоянно обновляет bar. Второй поток ожидает, пока bar будет прочитано как некоторое значение (скажем, просто вращается в цикле while(...)). Условие гонки состоит в том, что второй поток считывает значение bar до того, как первый поток снова обновит его. Хотя любой из этих вариантов гарантирует, что все потоки будут иметь одну и ту же внутреннюю ссылку (хотя ссылки являются атомарными независимо), это не имеет значения, работают ли они над объектом в разное время. -ломтики. И да, именно поэтому я предпочитаю неизменяемые объекты. - person Clockwork-Muse; 25.02.2013
comment
@Matt Ball, если вместо этого поле String было изменяемым, то volatile не делало класс потокобезопасным? - person gstackoverflow; 02.02.2017

Вы устанавливаете ссылки, и поэтому неизменность String не играет роли. Вы не влияете на содержимое String.

person Brian Agnew    schedule 25.02.2013

Нет, не безопасно.

Это изменяемое поведение Foo; Неизменяемость String не распространяется на Foo.

public class Foo{
    private String bar;

    public synchronized String getBar(){
        return bar;
    }

    public synchronized void setBar(String bar){
        this.bar = bar;
    }
}
person duffymo    schedule 25.02.2013
comment
+1 Как насчет синхронизации getBar()? Возможно, какой-то поток устанавливает новое значение bar, в то время как другие потоки читают bar. - person Eng.Fouad; 25.02.2013
comment
Извините, вы не имеете смысла для меня. Я думаю, что ваши комментарии только запутывают проблему. - person duffymo; 25.02.2013
comment
@duffymo гарантирует ли несинхронизированный геттер с синхронизированным сеттером согласованность между потоками? - person Matt Ball; 25.02.2013
comment
@duffymo, поскольку он у вас есть сейчас, геттер должен быть синхронизирован, чтобы гарантировать, что текущее состояние bar будет видно всем потокам. - person Steve Kuo; 26.02.2013

Нет, это не потокобезопасно.

Хотя String неизменяемо, проблема связана с полем Foo. Чтобы сделать это более очевидным, рассмотрим, например, метод, задачей которого будет добавлять (а не заменять) значение bar. Когда он вызывается из нескольких потоков, некоторые записи могут быть потеряны. То же самое (потеря записи) может произойти и с вашим простым сеттером, даже если в данном случае это изначально не очевидно.

person Theodoros Chatzigiannakis    schedule 25.02.2013