Итерация и fail-fast семантика
Синхронизированные обертки не решают проблему итерации:
Для безопасной итерации требуется внешняя синхронизация:
ConcurrentHashMap: Современный подход к параллельным отображениям
ConcurrentHashMap представляет собой фундаментально иную философию по сравнению с синхронизированными обертками.
Вместо блокировки всей структуры используется комбинация:
Fine-grained locking (тонкозернистые блокировки)
Lock-free алгоритмы для чтения
CAS операции (Compare-And-Swap)
Сегментирование (в версиях до Java 8)
В Java 8 архитектура была полностью переработана:
Инновации:
CAS для вставки: sun.misc.Unsafe.compareAndSwapObject
Tree bins: Преобразование в красно-черные деревья при длинных цепочках
Параллельные операции: forEach, search, reduce
Memory model и happens-before
ConcurrentHashMap обеспечивает строгие гарантии memory ordering:
Atomicity guarantees
Параллельные операции bulk
Параметризация параллелизма
#Java #для_новичков #beginner #immutability #Collection #synchronizedList #ConcurrentHashMap #CopyOnWriteArrayList
Синхронизированные обертки не решают проблему итерации:
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// ОПАСНО: ConcurrentModificationException все еще возможен
for (String item : syncList) {
// Другой поток может модифицировать список
syncList.remove("someItem"); // Из другого потока
}
Для безопасной итерации требуется внешняя синхронизация:
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// Безопасная итерация
synchronized (syncList) {
Iterator<String> it = syncList.iterator();
while (it.hasNext()) {
String item = it.next();
process(item);
}
}
ConcurrentHashMap: Современный подход к параллельным отображениям
ConcurrentHashMap представляет собой фундаментально иную философию по сравнению с синхронизированными обертками.
Вместо блокировки всей структуры используется комбинация:
Fine-grained locking (тонкозернистые блокировки)
Lock-free алгоритмы для чтения
CAS операции (Compare-And-Swap)
Сегментирование (в версиях до Java 8)
В Java 8 архитектура была полностью переработана:
// Концептуальная структура с Java 8
public class ConcurrentHashMap<K,V> {
volatile Node<K,V>[] table;
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
}
static final class TreeNode<K,V> extends Node<K,V> {
TreeNode<K,V> parent;
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev;
boolean red;
}
}
Инновации:
CAS для вставки: sun.misc.Unsafe.compareAndSwapObject
Tree bins: Преобразование в красно-черные деревья при длинных цепочках
Параллельные операции: forEach, search, reduce
Memory model и happens-before
ConcurrentHashMap обеспечивает строгие гарантии memory ordering:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// Поток 1
map.put("key", 42); // Запись с memory barrier
// Путок 2
Integer value = map.get("key"); // Чтение с happens-before гарантиями
// Гарантированно увидит 42, если нет перезаписи
Atomicity guarantees
// Атомарные операции
map.putIfAbsent(key, value); // Вставить если отсутствует
map.replace(key, oldValue, newValue); // Заменить если совпадает
map.compute(key, (k, v) -> v == null ? 1 : v + 1); // Атомарное вычисление
Параллельные операции bulk
ConcurrentHashMap<String, Long> wordCounts = new ConcurrentHashMap<>();
// Параллельный forEach
wordCounts.forEach(1, // Параллелизм
(key, value) -> System.out.println(key + ":" + value));
// Поиск
String result = wordCounts.search(1,
(key, value) -> value > 1000 ? key : null);
// Свертка
long total = wordCounts.reduceValues(1, Long::sum);
Параметризация параллелизма
ConcurrentHashMap<String, Data> map = new ConcurrentHashMap<>(
16, // initial capacity
0.75f, // load factor
8 // concurrency level (оценочное количество потоков)
);
#Java #для_новичков #beginner #immutability #Collection #synchronizedList #ConcurrentHashMap #CopyOnWriteArrayList
👍4
Производительность в различных сценариях
Для сценариев с частым чтением ConcurrentHashMap показывает исключительную производительность:
Чтение полностью lock-free
Минимальный contention между читателями
Эффективное использование кэшей процессора
При частой записи производительность зависит от:
Качества хэш-функции
Количества коллизий
Наличия tree bins
Конкуренции за конкретные бакеты
В конкурентной среде точный размер постоянно меняется. ConcurrentHashMap использует приближенные методы:
CopyOnWriteArrayList: Оптимизация для read-mostly сценариев
CopyOnWriteArrayList основан на фундаментальном компромиссе: дорогая запись в обмен на безопасное и эффективное чтение. Этот подход заимствован из систем управления памятью и файловых систем, где копирование при записи является стандартным паттерном.
Архитектурные принципы
Неизменяемое состояние
Ключевые особенности:
Массив объявлен как volatile для обеспечения memory visibility
Все операции чтения работают с текущим массивом
Операции записи создают новую копию
Гарантии consistency
Итераторы обеспечивают strong consistency для snapshot:
Видят состояние на момент создания
Никогда не выбрасывают ConcurrentModificationException
Не поддерживают операцию remove() (UnsupportedOperationException)
Практические паттерны использования
Event listeners и наблюдатели
Кэширование конфигураций
Ограничения и альтернативы
Когда не использовать CopyOnWriteArrayList
Частые модификации: Большие коллекции с частыми изменениями
Реальные требования: Когда нужны актуальные данные, а не snapshot
Ограничения памяти: Когда копирование больших массивов непозволительно
Альтернативные подходы
#Java #для_новичков #beginner #immutability #Collection #synchronizedList #ConcurrentHashMap #CopyOnWriteArrayList
Для сценариев с частым чтением ConcurrentHashMap показывает исключительную производительность:
Чтение полностью lock-free
Минимальный contention между читателями
Эффективное использование кэшей процессора
При частой записи производительность зависит от:
Качества хэш-функции
Количества коллизий
Наличия tree bins
Конкуренции за конкретные бакеты
В конкурентной среде точный размер постоянно меняется. ConcurrentHashMap использует приближенные методы:
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
// Приближенный размер (O(1), но может быть неточным)
int approximateSize = map.size();
// Более точный (но дорогой) подсчет
int exactSize = map.mappingCount(); // Java 8+
// Проверка пустоты (эффективная)
boolean isEmpty = map.isEmpty();
CopyOnWriteArrayList: Оптимизация для read-mostly сценариев
CopyOnWriteArrayList основан на фундаментальном компромиссе: дорогая запись в обмен на безопасное и эффективное чтение. Этот подход заимствован из систем управления памятью и файловых систем, где копирование при записи является стандартным паттерном.
Архитектурные принципы
Неизменяемое состояние
public class CopyOnWriteArrayList<E> {
private transient volatile Object[] array;
final Object[] getArray() {
return array;
}
final void setArray(Object[] a) {
array = a;
}
}Ключевые особенности:
Массив объявлен как volatile для обеспечения memory visibility
Все операции чтения работают с текущим массивом
Операции записи создают новую копию
Гарантии consistency
Итераторы обеспечивают strong consistency для snapshot:
Видят состояние на момент создания
Никогда не выбрасывают ConcurrentModificationException
Не поддерживают операцию remove() (UnsupportedOperationException)
Практические паттерны использования
Event listeners и наблюдатели
public class EventPublisher {
private final CopyOnWriteArrayList<EventListener> listeners =
new CopyOnWriteArrayList<>();
public void addListener(EventListener listener) {
listeners.add(listener); // Безопасно даже во время уведомлений
}
public void publish(Event event) {
for (EventListener listener : listeners) {
// Итерация по snapshot - безопасна
listener.onEvent(event);
}
}
}Кэширование конфигураций
public class ConfigurationCache {
private volatile CopyOnWriteArrayList<Config> cache;
public ConfigurationCache() {
cache = new CopyOnWriteArrayList<>();
}
public void refresh() {
List<Config> newConfigs = loadConfigs();
// Атомарная замена всего кэша
cache = new CopyOnWriteArrayList<>(newConfigs);
}
public List<Config> getConfigs() {
return cache; // Безопасное чтение
}
}Ограничения и альтернативы
Когда не использовать CopyOnWriteArrayList
Частые модификации: Большие коллекции с частыми изменениями
Реальные требования: Когда нужны актуальные данные, а не snapshot
Ограничения памяти: Когда копирование больших массивов непозволительно
Альтернативные подходы
// Для частых модификаций
List<String> frequentWrites = Collections.synchronizedList(new ArrayList<>());
// Для mixed workloads
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
// Для сценариев с преобладанием чтения
List<String> readMostly = new CopyOnWriteArrayList<>();
#Java #для_новичков #beginner #immutability #Collection #synchronizedList #ConcurrentHashMap #CopyOnWriteArrayList
👍3
Типичные ошибки многопоточного программирования с коллекциями
ConcurrentModificationException: Анатомия ошибки
ConcurrentModificationException возникает при обнаружении структурных изменений коллекции во время итерации.
Механизм основан на сравнении счетчика модификаций:
Типичные сценарии возникновения
Сценарий 1: Модификация во время итерации в одном потоке
Сценарий 2: Конкурентная модификация в разных потоках
NullPointerException в многопоточном контексте
NullPointerException в многопоточных сценариях часто является следствием race conditions, а не просто нулевых ссылок:
Классический антипаттерн с небезопасной публикацией:
Разные коллекции по-разному обрабатывают null:
#Java #для_новичков #beginner #immutability #Collection #synchronizedList #ConcurrentHashMap #CopyOnWriteArrayList
ConcurrentModificationException: Анатомия ошибки
ConcurrentModificationException возникает при обнаружении структурных изменений коллекции во время итерации.
Механизм основан на сравнении счетчика модификаций:
// Внутренний механизм ArrayList
protected transient int modCount = 0;
// В итераторе
int expectedModCount = modCount;
void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
Типичные сценарии возникновения
Сценарий 1: Модификация во время итерации в одном потоке
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String item : list) { // Создается итератор
if (item.equals("B")) {
list.remove(item); // modCount++ → исключение!
}
}Сценарий 2: Конкурентная модификация в разных потоках
// Поток 1
for (String item : sharedList) {
process(item); // Итерация
}
// Поток 2
sharedList.add("new"); // ConcurrentModificationException в потоке 1
NullPointerException в многопоточном контексте
NullPointerException в многопоточных сценариях часто является следствием race conditions, а не просто нулевых ссылок:
public class UnsafeCache {
private Map<String, Data> cache = new HashMap<>();
public Data get(String key) {
Data data = cache.get(key);
if (data == null) {
data = loadData(key); // Дорогая операция
cache.put(key, data); // Race condition!
}
return data; // Может вернуть null
}
}Классический антипаттерн с небезопасной публикацией:
public class BrokenSingleton {
private static Data instance;
public static Data getInstance() {
if (instance == null) { // Первая проверка (без синхронизации)
synchronized (BrokenSingleton.class) {
if (instance == null) { // Вторая проверка
instance = new Data(); // Небезопасная публикация!
}
}
}
return instance; // Может вернуть частично инициализированный объект
}
}Разные коллекции по-разному обрабатывают null:
// ConcurrentHashMap: запрещает null
ConcurrentHashMap<String, String> chm = new ConcurrentHashMap<>();
chm.put("key", null); // NullPointerException
// CopyOnWriteArrayList: разрешает null
CopyOnWriteArrayList<String> cowal = new CopyOnWriteArrayList<>();
cowal.add(null); // Допустимо
// Collections.synchronizedList: зависит от оборачиваемой коллекции
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
syncList.add(null); // Допустимо для ArrayList
#Java #для_новичков #beginner #immutability #Collection #synchronizedList #ConcurrentHashMap #CopyOnWriteArrayList
👍2
Race conditions и data races
Race condition: Неправильное поведение из-за непредсказуемого порядка выполнения
Data race: Одновременный доступ к shared memory без proper synchronization
Пример race condition
Пример data race
Deadlock, livelock и starvation
Deadlock с коллекциями
Livelock в конкурентных алгоритмах
Starvation в synchronized коллекциях
#Java #для_новичков #beginner #immutability #Collection #synchronizedList #ConcurrentHashMap #CopyOnWriteArrayList
Race condition: Неправильное поведение из-за непредсказуемого порядка выполнения
Data race: Одновременный доступ к shared memory без proper synchronization
Пример race condition
public class Counter {
private int count;
public void increment() {
count++; // Неатомарная операция: read-modify-write
}
}
// Два потока вызывают increment() 1000 раз каждый
// Ожидаемый результат: 2000
// Фактический результат: что угодно между 1000 и 2000Пример data race
public class VisibilityProblem {
private boolean ready = false;
private int value;
// Поток 1
public void writer() {
value = 42;
ready = true; // Без happens-before!
}
// Путок 2
public void reader() {
if (ready) {
System.out.println(value); // Может увидеть 0 вместо 42!
}
}
}Deadlock, livelock и starvation
Deadlock с коллекциями
// Классический deadlock с synchronizedList
List<String> list1 = Collections.synchronizedList(new ArrayList<>());
List<String> list2 = Collections.synchronizedList(new ArrayList<>());
// Поток 1
synchronized (list1) {
synchronized (list2) { // Ждет list2
// Критическая секция
}
}
// Путок 2 (обратный порядок)
synchronized (list2) {
synchronized (list1) { // Ждет list1 → DEADLOCK!
// Критическая секция
}
}
Livelock в конкурентных алгоритмах
public class LivelockExample {
private final ConcurrentHashMap<String, Boolean> locks =
new ConcurrentHashMap<>();
public void process(String key) {
// Бесконечные попытки захвата "локера"
while (!locks.putIfAbsent(key, true)) {
Thread.yield(); // Livelock: постоянно уступаем, но не прогрессируем
}
try {
// Работа с ресурсом
} finally {
locks.remove(key);
}
}
}Starvation в synchronized коллекциях
// Поток, постоянно читающий
synchronized (sharedList) {
// Долгая операция чтения
processAllElements(sharedList);
}
// Другие потоки не могут получить доступ для записи
// → Starvation писателей
#Java #для_новичков #beginner #immutability #Collection #synchronizedList #ConcurrentHashMap #CopyOnWriteArrayList
👍2
Что выведет код?
#Tasks
import java.util.concurrent.*;
public class Task301225 {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
Integer result = map.computeIfAbsent("b", k -> {
map.put("b", 2);
return map.get("b");
});
System.out.println(result);
System.out.println(map.get("b"));
}
}
#Tasks
👍1
👍1
Вопрос с собеседований
Почему volatile не заменяет synchronized?🤓
Ответ:
volatile гарантирует видимость изменений, но не атомарность составных операций.
Например, count++ небезопасен даже с volatile. synchronized обеспечивает и видимость, и взаимное исключение, но дороже по производительности.
#собеседование
Почему volatile не заменяет synchronized?
Ответ:
Например, count++ небезопасен даже с volatile. synchronized обеспечивает и видимость, и взаимное исключение, но дороже по производительности.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История IT-технологий сегодня — 31 декабря
ℹ️ Кто родился в этот день
Не нашел(
🌐 Знаковые события
1879 – Томас Эдисон впервые демонстрирует публике лампы накаливания в Менло-Парке, штат Нью-Джерси.
1983 – Компания AT&T Bell System была расформирована правительством Соединенных Штатов .
#Biography #Birth_Date #Events #31Декабря
Не нашел(
1879 – Томас Эдисон впервые демонстрирует публике лампы накаливания в Менло-Парке, штат Нью-Джерси.
1983 – Компания AT&T Bell System была расформирована правительством Соединенных Штатов .
#Biography #Birth_Date #Events #31Декабря
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Друзья и подписчики!
Вот и пришёл 2026 год🎄
Нам вновь даны 365 дней — для достижения целей и задач, для реализации смелых надежд и по-настоящему больших планов.
В этот день, который символизирует начало нового года, особенно приятно осознавать, что мы вместе. 🤝
Да, у нас небольшое и не самое активное сообщество, но я по-прежнему верю: нас объединяет стремление становиться лучше и сильнее, профессионально расти, быть более востребованными и со временем — действительно незаменимыми специалистами.
Я искренне благодарен каждому, кто участвует в жизни канала: ставит лайки, пишет комментарии и приходит на встречи.❤️
Именно для Вас канал продолжает жить и развиваться — без этого он давно бы умер.
Я твёрдо уверен, что когда наши парни дожмут врага на фронте и мы победим (а это неизбежно)💪 , компании перестанут жить в режиме неопределённости. Страх сменится расчётом и рынок начнёт постепенно оживать. Вакансий станет больше, требования — адекватнее, а ценность реальной экспертизы снова выйдет на первый план.
И именно те, кто уже сегодня системно наращивает знания и практику, окажутся максимально востребованными в этот момент.
И именно поэтому, несмотря на все штормы и бури рынка вакансий, засилье автоматических фильтров, роботов и ЧСВшных HR, я хочу, чтобы в 2026 году каждый из вас окончательно и бесповоротно покорил Java и получил тот самый оффер на 500k в секунду😎
Пусть это звучит иронично, но каждое усилие, каждый разобранный класс и каждая изученная библиотека реально приближают вас к этой цели.
В первые минуты нового года, оставляя за спиной трудности и сомнения, хочется пожелать вам простых, но важных вещей — удачи и терпения.👌
Удача пригодится при откликах на вакансии, а терпение — при подготовке и прохождении собеседований.😄
Желаю всем здоровья, счастья, любви близких и надёжной поддержки друзей.
Пусть 2026 год принесёт не только профессиональные достижения, но и внутреннюю гармонию, ощущение устойчивости и уверенности в выбранном пути.
С Новым годом!🌲
Вот и пришёл 2026 год
Нам вновь даны 365 дней — для достижения целей и задач, для реализации смелых надежд и по-настоящему больших планов.
В этот день, который символизирует начало нового года, особенно приятно осознавать, что мы вместе. 🤝
Да, у нас небольшое и не самое активное сообщество, но я по-прежнему верю: нас объединяет стремление становиться лучше и сильнее, профессионально расти, быть более востребованными и со временем — действительно незаменимыми специалистами.
Я искренне благодарен каждому, кто участвует в жизни канала: ставит лайки, пишет комментарии и приходит на встречи.
Именно для Вас канал продолжает жить и развиваться — без этого он давно бы умер.
Я твёрдо уверен, что когда наши парни дожмут врага на фронте и мы победим (а это неизбежно)
И именно те, кто уже сегодня системно наращивает знания и практику, окажутся максимально востребованными в этот момент.
И именно поэтому, несмотря на все штормы и бури рынка вакансий, засилье автоматических фильтров, роботов и ЧСВшных HR, я хочу, чтобы в 2026 году каждый из вас окончательно и бесповоротно покорил Java и получил тот самый оффер на 500k в секунду
Пусть это звучит иронично, но каждое усилие, каждый разобранный класс и каждая изученная библиотека реально приближают вас к этой цели.
В первые минуты нового года, оставляя за спиной трудности и сомнения, хочется пожелать вам простых, но важных вещей — удачи и терпения.
Удача пригодится при откликах на вакансии, а терпение — при подготовке и прохождении собеседований.
Желаю всем здоровья, счастья, любви близких и надёжной поддержки друзей.
Пусть 2026 год принесёт не только профессиональные достижения, но и внутреннюю гармонию, ощущение устойчивости и уверенности в выбранном пути.
С Новым годом!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11🍾3👍1👎1🤓1🆒1 1
История IT-технологий сегодня — 01 января
ℹ️ Кто родился в этот день
Ива́н Алексе́евич Вышнегра́дский (20 декабря 1831 [1 января 1832], Вышний Волочёк — 25 марта [6 апреля] 1895, Санкт-Петербург) — русский учёный-механик и государственный деятель. Основоположник теории автоматического регулирования, почётный член Петербургской АН (1888)
🌐 Знаковые события
1801 – Джузеппе Пиацци открывает Цереру , самый большой и первый известный объект в поясе астероидов.
1983 — ARPANET сменила основной протокол с NCP на TCP/IP, что и привело к появлению современного Интернета.
#Biography #Birth_Date #Events #01Января
Ива́н Алексе́евич Вышнегра́дский (20 декабря 1831 [1 января 1832], Вышний Волочёк — 25 марта [6 апреля] 1895, Санкт-Петербург) — русский учёный-механик и государственный деятель. Основоположник теории автоматического регулирования, почётный член Петербургской АН (1888)
1801 – Джузеппе Пиацци открывает Цереру , самый большой и первый известный объект в поясе астероидов.
1983 — ARPANET сменила основной протокол с NCP на TCP/IP, что и привело к появлению современного Интернета.
#Biography #Birth_Date #Events #01Января
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2