Как безопасно установить / получить строку в Java?

#java #string #thread-safety #immutability #reentrantlock

#java #строка #потокобезопасность #неизменяемость #блокировка повторного входа

Вопрос:

Я прочитал, что Java String class является неизменяемым и потокобезопасным, но я все еще не уверен, является ли назначение ссылки для строк потокобезопасным или нет.

Первый вопрос: если поток A вызывает Foo.setString() во время вызова потока B Foo.getString() , является ли следующий код потокобезопасным?

 Class Foo {
    String aString;
    public String getString() {
        return aString;
    }
    public void setString(s) {
        aString = s;
    }
}
  

Второй вопрос: если приведенный выше код не является потокобезопасным, используя блокировку повторного входа, как мне написать Foo.getString() метод?

 Class Foo {
    String aString;
    ReentrantLock aLock;
    public String getString() {
        aLock.lock();
        return aString;
        aLock.unlock(); // This line will be unreachable. How to fix??
    }
    public void setString(s) {
        aLock.lock();
        aString = s;
        aLock.unlock();
    }
}
  

Я должен использовать блокировку повторного входа, потому что мне нужно использовать функцию tryLock (тайм-аут).

Комментарии:

1. Примет ответ, если будут даны ответы на оба моих вопроса. Спасибо.

Ответ №1:

Вопрос 1: Это зависит от того, что вы подразумеваете под потокобезопасностью, Эрик Липперт написал очень хороший пост в блоге об этом здесь… Но объявление aString как volatile будет означать, что любые чтения в разных потоках гарантированно будут правильными.

Вопрос 2:

Используйте конструкцию try … finally, разблокировку в блоке finally, например:

 public String getString() {
  try {        
    aLock.lock();
    return aString;
  } finally {
    aLock.unlock();
  }
}

public void setString(s) {
  try {        
    aLock.lock();
    aString = s;
  } finally {
    aLock.unlock();
  }
}
  

Комментарии:

1. Также хотелось бы узнать больше о моем первом вопросе.

Ответ №2:

Создание aString volatile — это все, что вам нужно в этом случае (т. Е. При чтении после записи всегда будет отображаться самое новое значение) — синхронизация метода не особенно полезна, поскольку мы выполняем только один оператор — в обоих случаях, так что вам все равно понадобится синхронизация более высокого уровня, если вы хотите выполнить больше работы (скажем, «Если строка равна XYZ, то назначьте ей XYA»). Т.е. и synchronized, и volatile дают вам одинаковые гарантии (за исключением очевидной разницы, если мы используем synchronized в другом месте класса)

Таким образом, volatile достаточно и более производителен, но synchronized так же хорош — единственная разница в производительности, которую вы могли заметить, заключалась в том, что у вас было много конкурирующих обращений, но в остальном это не имеет значения — synchronized чрезвычайно оптимизирован для неконкурентоспособных обращений, по крайней мере, в Hotspot (но я предполагаю, что то же самое верно для всех современных JVM).

Ответ №3:

Используйте ключевое слово synchronized :

    public synchronized String getString() {
        return aString;
    }

    public synchronized void setString(s) {
        aString = s;
    }
  

Комментарии:

1. Извините, в моем случае я должен использовать блокировку повторного входа, потому что мне нужно использовать функцию tryLock (тайм-аут).

2. ок, не заметил. Так что лучше разблокировать в блоке finally, как предлагал другой плакат.

3. Зачем вам нужно использовать tryLock? Это домашнее задание или что-то подобное?