#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? Это домашнее задание или что-то подобное?