Безопасно ли строить объект следующим образом в многопоточной среде? - PullRequest
4 голосов
/ 11 марта 2019
public class DataEvent {
    private static final AtomicInteger lastRevision = new AtomicInteger();
    private final int revision;
    private final long threadId;
    private final long timestamp;

    private DataEvent(int revision) {
        this.revision = revision;
        this.threadId = Thread.currentThread().getId();
        this.timestamp = System.nanoTime();
    }

    public static DataEvent newInstance() {
        return new DataEvent(lastRevision.incrementAndGet());
    }
}

Мои вопросы следующие:

  • абсолютно правильно ли говорить, что все объекты будут строиться последовательно один за другим? Я имею в виду, что каждый новый объект создается позже, чем предыдущий. Другими словами, каждый новый объект имеет timestamp, который больше, чем предыдущий.
  • как ключевое слово final влияет на это поведение? Как я понимаю, если все поля объекта final, то это делает конструктор атомарным в некотором роде. Ссылка не публикуется до тех пор, пока не будут инициализированы все поля final.
  • какова лучшая практика для создания таких объектов? Достаточно ли сделать lastRevision атомным или newInstance должно быть объявлено как synchronized?

Ответы [ 2 ]

2 голосов
/ 11 марта 2019

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

Нет.lastRevision.incrementAndGet() является блокирующим вызовом, но планировщик может приостановить первый поток после получения первого идентификатора, а затем возобновить его после того, как второй поток получит второй идентификатор, что означает, что оба конструктора будут выполняться одновременно.

Другими словами, каждый новый объект имеет метку времени, которая больше предыдущей.

Нет, см. Выше.

как final ключевое слово влияет на это поведение?

Это не так.

Как я понимаю, если все поля объекта являются окончательными, то это делает конструктор атомарным в некотором роде

Неверно.Если каждое поле является окончательным, тогда класс является неизменным *, что делает его неявно поточно-ориентированным.

Каков наилучший способ создания таких объектов?Достаточно ли сделать lastRevision атомарным или newInstance должен быть объявлен синхронизированным?

Если каждый экземпляр должен создаваться последовательно, тогда newInstance должен синхронизироваться.Как только это так, нет смысла в атомном целом числе.Даже если вы сделаете это, временная метка может оставаться неизменной в зависимости от разрешения системных часов.


* ну, не совсем так.Если каждое поле является окончательным и также является неизменяемым .

1 голос
/ 11 марта 2019
  • Гарантии атомарности действительны только для incrementAndGet() звонка.Так что да, ревизии будут последовательными для каждого нового объекта, что является целью атомарных типов данных.Итак, чтобы ответить на ваш вопрос: нет гарантии , что несколько потоков будут выполнять операторы внутри конструктора в том же порядке, в котором они вызывают incrementAndGet().Для этого вам нужно поместить этот раздел в блок synchronized.

  • final с здесь не помогут.Это чисто логическая функция, которая не позволяет изменять поле после создания объекта.

  • Если вам действительно нужно, чтобы временные метки были выровнены с ревизиями, вам необходимо ввести некоторую форму синхронизации потоков.Вы можете синхронизировать весь метод, и тогда lastRevision не обязательно будет атомарным.

Кроме того, что бы вы ни решили сделать, вы также можете проверить гарантии, предоставленные System.nanoTime()сам. Нажмите .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...