JavaRush /Курсы /Java Multithreading /Канкаренси, BlockingQueues (java7)

Канкаренси, BlockingQueues (java7)

Java Multithreading
6 уровень , 9 лекция
Открыта
Канкаренси, BlockingQueues (java7) - 1

— Привет, Амиго!

— Привет, Ким!

— Я тебе сегодня расскажу про «канкаренси» — Concurrency.

Concurrency – это библиотека классов в Java, в которой собрали специальные классы, оптимизированные для работы из нескольких нитей. Эта тема очень интересная и обширная. Но сегодня мы просто познакомимся с ней. Эти классы собраны в пакете java.util.concurrent. Я расскажу про пару интересных классов.

Атомарные типы.

Ты уже знаешь, что даже операция count++ не является потокобезопасной (thread-safe). При увеличении переменной на 1 реально происходит три операции, в результате чего может возникнуть конфликт при одновременном изменении этой переменной.

— Ага, Элли рассказывала это немного раньше:

Нить 1 Нить 2 Результат
register1 = count; register1++;
count = register1;
register2 = count; register2++;
count = register2;
register1 = count;
register2 = count; register2++; count = register2;
register1++; count = register1;

— Именно. Тогда в Java были добавлены типы данных, которые выполняют такие операции неразрывно – атомарно. (Атом — неделимый).

Так в Java появились типы AtomicInteger, AtomicBoolean, AtomicDouble и т.д.

Вот, допустим нам нужно сделать класс «счетчик»:

Пример
class Counter
{
 private int c = 0;

 public void increment() 
 {
  c++;
 }

 public void decrement() 
 {
  c--;
 }

 public int value() 
 {
  return c;
 }
}

Как бы ты сделал его объекты thread-safe?

— Да, сделал бы все методы synchronized и все:

Пример
class synchronized Counter 
{
 private int c = 0;

 public synchronized void increment() 
 {
  c++;
 }

 public synchronized void decrement() 
 {
  c--;
 }

 public synchronized int value() 
 {
  return c;
 }
}

— Отличная работа. А вот, как бы он выглядел с использованием атомарных типов:

Пример
class AtomicCounter
{
 private AtomicInteger c = new AtomicInteger(0);

 public void increment() 
 {
  c.incrementAndGet();
 }

 public void decrement() 
 {
  c.decrementAndGet();
 }

 public int value() 
 {
  return c.get();
 }
}

И твой и мой классы работают одинаково, но класс с AtomicInteger работает быстрее.

— Т.е. разница небольшая?

Да. Из своего опыта я могу посоветовать всегда в лоб использовать synchronized. И только когда весь код приложения уже написан и начинается процесс его оптимизации, то можно начинать переписывать его части с использованием Atomic-типов. Но в любом случае, я бы хотела, чтобы ты знал, что такие типы есть. Даже если не будешь активно ими пользоваться, ты всегда можешь увидеть код, написанный с их применением.

— Согласен, в это есть смысл.

— Кстати, а ты заметил, что атомарные типы – не immutable? AtomicInteger, в отличии от просто Integer, содержит методы по изменению своего внутреннего состояния.

— Понятно, прямо как String и StringBuffer.

— Да, что-то типа того.

Нитебезопасные (иногда называют — потокобезопасные) коллекции.

В качестве такой коллекции я хотела бы привести ConcurrentHashMap. Как сделать HashMap thread-safe?

— Сделать все его методы synchronized?

— Да, но представь теперь, что у тебя есть один такой SynchronizedHashMap, а к нему обращаются десятки нитей. И сто раз в секунду в этот map добавляется новая запись, при этом весь объект блокируется для чтения и записи.

— Да, но это же стандартный подход. Что тут можно сделать?

— Разработчики Java придумали несколько крутых штук.

Во-первых, они хранят данные в ConcurrentHashMap не одним куском, а разбивая их на порции — «корзины». И когда кто-то меняет данные в ConcurrentHashMap, то блокируется не весь объект, а только одна корзина, к которой происходит доступ. Т.е. на самом деле объект могут одновременно менять много нитей.

Во-вторых, помнишь проблему, что нельзя одновременно идти по элементам списка/мэпа и менять его? Такой код кинет исключение:

Нельзя одновременно идти в цикле по элементам коллекции и менять ее
HashMap<String, Integer> map = new HashMap<String, Integer>();

for (String key: map.keySet())
{
 if (map.get(key)==0)
  map.remove(key);
}

А в ConcurrentHashMap это можно:

Нельзя одновременно идти в цикле по элементам коллекции и менять ее
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();

for (String key: map.keySet())
{
 if (map.get(key)==0)
  map.remove(key);
}

У Concurrency есть очень много преимуществ. Просто надо очень хорошо разобраться в этих классах, чтобы их применять.

— Ясно. Спасибо, Ким. Действительно, очень интересные классы. Надеюсь, когда-нибудь я тоже буду виртуозно ими владеть.

Комментарии (98)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Gans Electro Уровень 51
12 июня 2023
GPT: В Java BlockingQueue представляет собой интерфейс, который расширяет Queue и предоставляет дополнительные операции, которые блокируют поток исполнения, если очередь полна или пуста. Этот интерфейс часто используется для реализации шаблона Producer-Consumer (производитель-потребитель) в параллельных приложениях. Ниже приведены некоторые из основных методов, определенных в интерфейсе BlockingQueue: 1. `put(E element)`: Добавляет элемент в конец очереди, если она не заполнена. Если очередь заполнена, поток исполнения блокируется до тех пор, пока место не освободится. 2. `take()`: Удаляет и возвращает элемент из начала очереди. Если очередь пуста, поток исполнения блокируется до тех пор, пока не появится элемент. 3. `offer(E element)`: Пытается добавить элемент в конец очереди. Если очередь заполнена, метод возвращает false, иначе true. 4. `poll()`: Удаляет и возвращает элемент из начала очереди. Если очередь пуста, метод возвращает null. 5. `size()`: Возвращает текущий размер очереди. 6. `isEmpty()`: Проверяет, пуста ли очередь. 7. `contains(Object o)`: Проверяет, содержит ли очередь указанный элемент. BlockingQueue имеет несколько реализаций в стандартной библиотеке Java, включая ArrayBlockingQueue, LinkedBlockingQueue и PriorityBlockingQueue. Каждая из этих реализаций имеет свои особенности, но общая концепция остается неизменной: блокировать потоки при попытке добавить элемент в полную очередь или извлечь элемент из пустой очереди. Пример использования BlockingQueue может выглядеть следующим образом:
Gans Electro Уровень 51
12 июня 2023

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;

public class ProducerConsumerExample {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

        // Producer
        Thread producer = new Thread(() -> {
            try {
                for (int i = 1; i <= 10; i++) {
                    queue.put(i);
                    System.out.println("Producer produced: " + i);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // Consumer
        Thread consumer = new Thread(() -> {
            try {
                while (true) {
                    int value = queue.take();
                    System.out.println("Consumer consumed: " + value);
                    Thread.sleep(2000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        producer.start();
        consumer.start();
    }
}
В этом примере создается ArrayBlockingQueue с емкостью 10 элементов. Поток производителя (`producer`) добавляет числа от 1 до 10 в очередь, а поток потребителя (`consumer`) извлекает их из очереди. Оба потока блокируются при попытке добавления в полную очередь или извлечения из пустой очереди.
Gans Electro Уровень 51
12 июня 2023
Некоторые распространенные реализации интерфейса BlockingQueue включают: ArrayBlockingQueue: Этот класс реализует ограниченную по размеру очередь, основанную на массиве. Он имеет фиксированную емкость и блокирует попытку добавления элемента в полную очередь или удаления элемента из пустой очереди. LinkedBlockingQueue: Этот класс реализует неограниченную по размеру очередь, основанную на связанном списке. Он может быть ограничен только максимальным значением памяти в системе. Если размер не указан при создании, по умолчанию он равен Integer.MAX_VALUE. PriorityBlockingQueue: Этот класс представляет приоритетную очередь, в которой элементы добавляются в соответствии с их приоритетом. Элементы должны быть сравнимыми, либо их необходимо снабдить компаратором. SynchronousQueue: Это особая реализация BlockingQueue, которая может содержать только один элемент. Она используется для обмена данными между двумя потоками, где один поток ожидает, пока другой не заберет элемент, и наоборот.
25 сентября 2024
Спасибо добрый человек
Rolik Уровень 41
22 апреля 2023
Короче, synchronized модификаторы и атомарные классы это только для long и double типов. Потому что они 64-х битные. И на 32-х битных ОС возможны баги. Это основное что надо запомнить. Да прибудет с вами JAVA 🙏
Kirill Уровень 46
20 октября 2024
Что за бред? И synchronized, и атомарные классы прекрасно работают и на 32-х разрядных системах. Для них и специальные 32-х битные JVM есть. И даже для 64-х рахрядных есть флаг запуска -d32
comrade_b Уровень 39
28 июня 2022
ВСЕ: Вот мы проходим Multithreading. Сейчас мы подробно изучим Concurrency, потому что потоко-безопасные методы этой утилиты (java.util.concurrent) использует весь современный мир, и ОООООооочень редко кто пишет сам синхронизированные классы и методы. JAVARUSH: Эта тема очень интересная и обширная. Но сегодня мы просто познакомимся с ней. ТАК ЖЕ JAVARUSH: Разберись с BlockingQueue. По образу и подобию класса Producer создай класс Consumer, который будет выводить данные из BlockingQueue в консоль. И СНОВА JAVARUSH: Вот вам интересная задача вычислить объем круглого мамонта в двумерном квадрате. Решите с помощью ExecutorService, который вы должны сами нагуглить.
Buenos Уровень 49
8 июля 2022
Ну хоть, благо, появился отдельный уровень по канкеррент в новом модуле JSP & Servlets, но последовательности не хватает
comrade_b Уровень 39
8 июля 2022
Ну так а что с того, что он появился? Я вот честно скажу: я посмотрел на Java Syntax Zero и пришел сюда чисто из-за того, что есть лекция и по ней есть задача. Это реальный инструмент для закрепления знаний. Иначе б я так на видео курсах бы и остался, где лучше все разжевывают. Но тут то мало того, что материал древний, его катострофически мало, так и задачи по материалу стали исчезать. В этом квесте должно быть 90% процентов задач по нитям, по конкаренту и всему, что с этим связано. Да, меня зацепила задача по чату. Но все ж остальное вообще не имеет ничего общего с квестом. Если б я хотел рандомные задачи порешать, я б пошел вон на codewars к примеру. Ну то есть я сейчас для себя должен буду решить задачу "как восполнить пробелы по нитям и потокобезопасным классам и методам". И теория, и практика.
comrade_b Уровень 39
8 июля 2022
Хотя конечно надо заглянуть что там в сервлетах по конкаренту. Но я очень сомневаюсь, что там больше 2-3 лекций.
Buenos Уровень 49
8 июля 2022
Я к тому, что не так давно и этого не было. А то, что в квесте по Multithreding сам этот мультитред появился только во второй половине квеста и то, пока только вроде то, что и так уже было, меня, конечно повергло в не сказать какое недоумение... Ну и допквест по сервлетам я, конечно, ожидал, что будет с задачами какими-то, там ни одной нет. 2 уровня чисто по паттернам - так и там информации с гулькин нос, типа есть такой, есть сякой. Это мне и без джавыраш рассказали... А в смысле задачи исчезают, реально куда-то исчезают?
comrade_b Уровень 39
9 июля 2022
Я неправильно выразился. Не то, чтобы задачи вырезались. Нет. Я имел в виду, что если в первыом квесте были задачи именно по лекциям. То во втором и третьем задачи уже просто от балды. И смешно, и грустно 😐 Ребята конечно молодцы, постарались, сделали 1 квест хороший. И все на это ведутся, цепляются, покупают вход. А внутри.... Жаль. Ожиданий было больше
Kurama Уровень 50
9 ноября 2022
Вот вам интересная задача вычислить объем круглого мамонта в двумерном квадрате. Объем равен нулю, т.к. круг и квадрат двумерны
Александр Уровень 43
9 ноября 2024
более того, он круглый только когда мы на него смотрим из трех-мерного пространства)), а глазами "местных" его соседей, он вообще отрезок... 😁
Zlata Уровень 49
9 мая 2022
+++++
The Body of the Atr Уровень 3
12 апреля 2022
в заголовке статьи есть BlockingQueues а в статье про это ничего нет
GvardeeZZZ Уровень 41
6 января 2022
Прочел "Целебезопасен"
Robert Imangulov Уровень 43
21 октября 2021
Сначала задачи, а потом лекция. Насколько такой подход целесообразен?
Уровень 51
23 ноября 2021
😂еще не привык?
Griseo Уровень 43
27 декабря 2021
Думаю нас пытаются приучить искать информацию и прибывать в стресовых ситуациях, когда задача есть, а темы касательно ее не пройдены, думаю в работе такое часто будет встречаться, но это не точно :D
SERGEY Уровень 31
2 февраля 2022
Целесообразен на столько, что без задач ты бы оставил комент "Херня, где это вообще применяется"
14 сентября 2021
Виталий Уровень 41
28 июня 2021
https://m.habr.com/ru/company/luxoft/blog/157273/
Mike-L Уровень 43
8 июня 2021
Картинка из лекции напомнила анекдот, про то как мужик себе операцию делал по удалению лишнего жира с живота. Просыпается мужик в палате после операции и зовёт сестру - Сестра, принесите мне зеркало. Хочу посмотреть результаты операции. Та приносит ему зеркало. Мужик смотрит в него. И возмещённо орет. - Что вы сделали со мной?! У меня же пупок на лбу!!! Сестра отвечает ему: -Зато какой у вас теперь галстук. пысы. вот и на лекциях также, началось за здравие и комиксы, а дальше вата и ГуглРаш ))