Нужно ли расширять класс до ConcurrentHashMap или у меня может быть переменная ConcurrentHashMap для threadSafety - PullRequest
0 голосов
/ 06 января 2019

Я создаю службу резервирования Server-Client на основе сокетов, и у меня возникла проблема с классом, к которому будут обращаться несколько потоков, нужно ли расширять ConcurrentHashMap или достаточно, чтобы переменная ConcurrentHashMap была безопасна для потоков?

У меня есть две идеи, но я не уверен, будет ли работать первая, поэтому первая будет создавать класс, который реализует только Serializable, имеет переменную дату, а затем переменную ConcurrentHashMap, с которой потоки хотят работать, вторая идея - иметь класс которая расширяет Concurrent Hash Map и представляет собой CHP, но с дополнительной переменной, чтобы она отличалась от других

public class Day implements Serializable {
private LocalDate date;
private ConcurrentHashMap<String, Boolean> schedule;

public Day(LocalDate date){
    this.date = date;
    this.schedule = new ConcurrentHashMap<>();
    IntStream.range(10, 18).forEachOrdered(
            n -> this.schedule.put(LocalTime.of(n, 0).toString(), TRUE));
}

public void changeaval(String key,Boolean status) {
    this.schedule.replace(key,status);
}

public boolean aval(String key){
    return this.schedule.get(key);
}

public LocalDate getDate(){return this.date;}

public ConcurrentHashMap getSchedule(){return this.schedule;}

}

Я просто хочу иметь класс / объект, к которому могут обращаться несколько потоков, и который можно отличить от других / сравнимый и который имеет ConcurrentHashMap, который отображает Int -> Boolean Это первый раз, когда я использую Stack, и это мой первый проект на Java, поэтому я не знаю, извините, если что-то не так.

Ответы [ 2 ]

0 голосов
/ 06 января 2019

При работе с объектами, доступ к которым осуществляется несколькими потоками, нужно обратить внимание на две вещи:

  1. Состояние гонки - из-за планирования потоков операционной системой и оптимизации переупорядочения команд компилятором инструкции выполняются в порядке, не предназначенном для программиста, что приводит к ошибкам
  2. Видимость памяти - В многопроцессорной системе изменения, сделанные одним процессором, не всегда сразу видны другим процессорам. Процессоры хранят данные в своих локальных регистрах и кэшах по соображениям производительности и поэтому невидимы для потоков, выполняемых другими процессорами.

К счастью, мы можем справиться с обеими этими ситуациями, используя правильную синхронизацию.

Давайте поговорим об этой конкретной программе.

Localdate сам по себе является неизменным и потокобезопасным классом. Если мы посмотрим на исходный код этого класса, то увидим, что все поля этого класса final. Это означает, что как только конструктор Localdate завершит инициализацию объекта, сам объект станет видимым в потоках. Но когда она присваивается ссылочной переменной в другом объекте, то нужно ли присваивать (другими словами, содержимое ссылочной переменной) другим потокам или нет, это то, что нам нужно искать.

Учитывая конструктор в вашем случае, мы можем обеспечить видимость поля date в потоках при условии date либо final, либо volatile. Поскольку вы не изменяете поле date в своем классе, вы вполне можете сделать его окончательным, и это обеспечит безопасную инициализацию. Если впоследствии вы решите использовать метод установки для этого поля (в зависимости от вашей бизнес-логики и вашего дизайна), вам нужно сделать поле volatile вместо final. volatile создает отношение случай-до , что означает, что любая инструкция, которая выполняется в конкретном потоке перед записью в переменную volatile, будет сразу же видна другим потокам, как только они прочитают то же самое переменная переменная.

То же самое относится к ConcurrentHashMap. Вы должны сделать поле schedule final. Поскольку ConcurrentHashMap содержит в себе все необходимые синхронизации, любое значение, установленное для ключа, будет видно другим потокам, когда они попытаются его прочитать.

Обратите внимание, однако, что если бы у вас были некоторые изменяемые объекты в качестве значений ConcurrentHashMap вместо Boolean, вам пришлось бы проектировать их так же, как указано выше.

Также может быть полезно знать, что существует концепция, называемая piggy-backing , которая означает, что если один поток записывает все свои поля, а затем записывает в переменную volatile, все записывается поток перед записью в переменную volatile будет виден другим потокам, при условии, что другие потоки сначала прочитают значение переменной volatile после ее записи первым потоком. Но когда вы делаете это, вы должны очень внимательно следить за последовательностью чтения и записи, и это подвержено ошибкам. Итак, это делается, когда вы хотите выжать последнее падение производительности из фрагмента кода, который встречается редко. Позитивная безопасность, ремонтопригодность, удобочитаемость до исполнения.

Наконец, в коде нет условия гонки. Единственная запись, которая происходит, происходит на ConcurrentHashMap, который сам по себе является потокобезопасным.

0 голосов
/ 06 января 2019

В принципе, оба подхода эквивалентны. С архитектурной точки зрения создание переменной внутри выделенного класса является предпочтительным из-за лучшего контроля над тем, какие методы доступны пользователю. При расширении пользователь может получить доступ ко многим методам лежащего в основе ConcurrentHashMap и злоупотреблять ими.

...