Java дважды проверил блокировку - строки

При условии strings содержит поле final, означает ли это, что в контексте блокировки с двойной проверкой нет необходимости объявлять их volatile? Например

class SomeClass{
     private String val;

     String getVal(){
           if(val == null){
                synchronized(this){
                      if(val ==null)
                           val = new String("foo");
                }
          }
     }
}

Я использовал строку в качестве примера, но она должна работать с другими объектами, которые объявляют какое-то окончательное поле, правильно?

2 ответа

Для строк вы правы. Строка, объявленная как final, не может быть отлична, и поэтому вам не нужно синхронизироваться при ее использовании.

Это не относится к другим объектам. Возьмите этот маленький класс, например:

public class BankAccount {
    private int balance = 0;
    public void addMoney(int money) {
        balance+=money;
    }
}

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

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

Нет, ты все еще должен объявить val как изменчиво здесь. Проблема в том, что пока String является неизменным и потокобезопасным, val не является. У вас все еще есть проблема с видимостью val сам.

Чтобы ответить на ваш вопрос о "учитывая, что String содержит конечное поле", обратите внимание, что JLS специально говорит, что видимость не транзитивна при работе с final поля.

Для записи w, замораживания f, действия a (которое не является чтением конечного поля), считывания r1 конечного поля, замороженного f, и считывания r2, такого что hb(w, f), hb(f, a), mc(a, r1) и разыменования (r1, r2), то при определении того, какие значения можно увидеть через r2, мы рассматриваем hb(w, r2). (Это происходит до того, как заказ не транзитивно завершается с другими заказами до).

https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html

Где "заморозить f" - это то, как JLS ссылается на потокобезопасную часть final семантика поля, т. е. часть, которая фактически делает объект, на который ссылается поле, видимым.

(Есть случаи, когда вы можете положиться на транзитивность с синхронизацией и случается раньше. Брайан Гетц называет это "контрейлерной поддержкой" и говорит об этом в Java Concurrency in Practice. Но это в основном только эксперты, и я не рекомендую пока вы не разберетесь с моделью памяти Java.)

Короче говоря, объявить val энергозависимы и не беспокойтесь о сохранении двух наносекунд, пропуская синхронизацию. Дополнительный ригмарол в коде не стоит, и все равно не работает.

Другие вопросы по тегам