Сколько экземпляров String будет создано в результате выполнения приведенного кода?
  Anonymous Quiz
    20%
    1
      
    67%
    2
      
    10%
    3
      
    3%
    4
      
    👍13🤔9🍌5🌭2😐2
  Класс ThreadLocal представляет хранилище тред-локальных переменных. По способу использования он похож на обычную обертку над значением, с методами get(), set() и remove() для доступа к нему, и дополнительным фабричным методом ThreadLocal.withInitial(), устанавливающим значение по-умолчанию.
Отличие тред-локальной переменной от обычной в том, что ThreadLocal хранит отдельную независимую копию значения для каждого ее использующего потока. Работа с такой переменной потокобезопасна.
Проще говоря, объект класса ThreadLocal хранит внутри не одно значение, а как бы хэш-таблицу поток➝значение, и при использовании обращается к значению для текущего потока.
Первый, самый очевидный вариант использования – данные, относящиеся непосредственно к треду, определенный пользователем «контекст потока». На скриншоте пример такого использования: ThreadId.get() вернет порядковый номер текущего треда.
Другой случай, с которым локальная переменная потока может помочь – кэширование read-only данных в многопоточной среде без дорогостоящей синхронизации.
Помимо обычного ThreadLocal, в стандартной библиотеке присутствует его расширение InheritableThreadLocal. Этот класс «наследует» значение – изначально берет его для потока, являющегося родителем текущего.
Отличие тред-локальной переменной от обычной в том, что ThreadLocal хранит отдельную независимую копию значения для каждого ее использующего потока. Работа с такой переменной потокобезопасна.
Проще говоря, объект класса ThreadLocal хранит внутри не одно значение, а как бы хэш-таблицу поток➝значение, и при использовании обращается к значению для текущего потока.
Первый, самый очевидный вариант использования – данные, относящиеся непосредственно к треду, определенный пользователем «контекст потока». На скриншоте пример такого использования: ThreadId.get() вернет порядковый номер текущего треда.
Другой случай, с которым локальная переменная потока может помочь – кэширование read-only данных в многопоточной среде без дорогостоящей синхронизации.
Помимо обычного ThreadLocal, в стандартной библиотеке присутствует его расширение InheritableThreadLocal. Этот класс «наследует» значение – изначально берет его для потока, являющегося родителем текущего.
👍19
  Сколько объектов станут доступны для сборщика мусора после выполнения строки 1?
  Anonymous Quiz
    32%
    1
      
    24%
    5
      
    38%
    6
      
    5%
    11
      
    👍26🤨10
  Как выполнить две задачи параллельно?
Простейший, путь – явно создать два объекта типа Thread, передать им инстансы Runnable, с нужными задачами в реализации их методов run, и запустить вызвав thread.start(). Если в основном потоке нужно дождаться завершения задач – после start() вызывается метод thread.join(). Исполнение зависнет на вызове этого метода до тех пор, пока тред не закончит свою задачу и не умрет. Вся работа задач с внешними данными должна быть синхронизирована.
Такое ручное создание тредов полезно в учебных целях, но считается плохой практикой в промышленном коде: само создание – дорогостоящая операция, а большое количество случайно созданных потоков может приводить к проблеме голодания (starvation) потоков.
В качестве продвинутой альтернативы используются пуллы потоков – реализации интерфейса ExecutorService. Такие сервисы создаются статическими фабричными методами класса Executors. Они умеют принимать задачи в виде Runnable- или Callable-объектов на заранее созданном наборе потоков (собственно, пулле).
Кроме самого пулла, экземпляры ExecutorService содержат фабрику потоков («инструкцию» как создать тред при необходимости), и коллекцию-очередь задач на исполнение.
В ответ на передачу на исполнение Runnable или Callable, сервис возвращает связанный с ним объект типа Future – хранилище, которое будет заполнено результатом выполнения задачи в будущем. Даже если никакого результата не ожидается, Future поможет дождаться момента завершения обработки задачи.
В Android для асинхронного выполнения используется похожая сущность – Looper.
Простейший, путь – явно создать два объекта типа Thread, передать им инстансы Runnable, с нужными задачами в реализации их методов run, и запустить вызвав thread.start(). Если в основном потоке нужно дождаться завершения задач – после start() вызывается метод thread.join(). Исполнение зависнет на вызове этого метода до тех пор, пока тред не закончит свою задачу и не умрет. Вся работа задач с внешними данными должна быть синхронизирована.
Такое ручное создание тредов полезно в учебных целях, но считается плохой практикой в промышленном коде: само создание – дорогостоящая операция, а большое количество случайно созданных потоков может приводить к проблеме голодания (starvation) потоков.
В качестве продвинутой альтернативы используются пуллы потоков – реализации интерфейса ExecutorService. Такие сервисы создаются статическими фабричными методами класса Executors. Они умеют принимать задачи в виде Runnable- или Callable-объектов на заранее созданном наборе потоков (собственно, пулле).
Кроме самого пулла, экземпляры ExecutorService содержат фабрику потоков («инструкцию» как создать тред при необходимости), и коллекцию-очередь задач на исполнение.
В ответ на передачу на исполнение Runnable или Callable, сервис возвращает связанный с ним объект типа Future – хранилище, которое будет заполнено результатом выполнения задачи в будущем. Даже если никакого результата не ожидается, Future поможет дождаться момента завершения обработки задачи.
В Android для асинхронного выполнения используется похожая сущность – Looper.
👍26
  Что выведет следующий код?
  Anonymous Quiz
    9%
    Long
      
    6%
    Number
      
    38%
    Object
      
    47%
    Произойдет ошибка компиляции
      
    👍13🐳4
  Как реализовать паттерн producer/consumer?
Шаблон producer/consumer (производитель/потребитель) – простая и базовая реализация обмена данными между несколькими потоками. Поток-производитель отправляет объекты на условную обработку, потоки-потребители асинхронно принимают и обрабатывают их.
Общий вид решения выглядит так. Продюсер отправляет объекты в специальную коллекцию – буфер. Когда потребитель освобождается, он отправляет запрос на извлечение одного объекта из буфера. Если буфер пуст, потребитель блокируется и ждет, если буфер переполнен – ждет производитель.
На практике реализовать этот паттерн можно множеством способов. Самый правильный способ для применения в бою – использовать готовую реализацию из стандартной библиотеки, объект типа BlockingQueue.
На собеседовании обычно просят реализовать паттерн с нуля. Реализация представлена на изображении. Модификатор synchronized делает так, чтобы в каждый момент времени мог выполняться только один из методов, и только одним потоком. Этого достаточно для корректной работы пока буфер не пуст и не полон. При пустом или полном буфере управление явно перебрасывается на производителя или потребителя соответственно, с помощью методов notify() и wait().
Шаблону producer/consumer посвящена глава 5.3 книги Java Concurrency in Practice.
Сильно упрощая, на основе этого паттерна работают сервисы-брокеры сообщений: Rabbit MQ, Apache ActiveMQ и другие.
Шаблон producer/consumer (производитель/потребитель) – простая и базовая реализация обмена данными между несколькими потоками. Поток-производитель отправляет объекты на условную обработку, потоки-потребители асинхронно принимают и обрабатывают их.
Общий вид решения выглядит так. Продюсер отправляет объекты в специальную коллекцию – буфер. Когда потребитель освобождается, он отправляет запрос на извлечение одного объекта из буфера. Если буфер пуст, потребитель блокируется и ждет, если буфер переполнен – ждет производитель.
На практике реализовать этот паттерн можно множеством способов. Самый правильный способ для применения в бою – использовать готовую реализацию из стандартной библиотеки, объект типа BlockingQueue.
На собеседовании обычно просят реализовать паттерн с нуля. Реализация представлена на изображении. Модификатор synchronized делает так, чтобы в каждый момент времени мог выполняться только один из методов, и только одним потоком. Этого достаточно для корректной работы пока буфер не пуст и не полон. При пустом или полном буфере управление явно перебрасывается на производителя или потребителя соответственно, с помощью методов notify() и wait().
Шаблону producer/consumer посвящена глава 5.3 книги Java Concurrency in Practice.
Сильно упрощая, на основе этого паттерна работают сервисы-брокеры сообщений: Rabbit MQ, Apache ActiveMQ и другие.
👍33🔥6
  Привет 👋 
Есть интересная задача для подписчиков нашего канала❔ 
Присылай нам сюда @MortySmlth 🤩
Что нужно указать:
1. Вопрос задачи
2. Код задачи или скрин + код
3. Варианты ответа
4. Правильный ответ
5. Объяснение правильного ответа(по желанию).
Есть интересная задача для подписчиков нашего канала
Присылай нам сюда @MortySmlth 🤩
Что нужно указать:
1. Вопрос задачи
2. Код задачи или скрин + код
3. Варианты ответа
4. Правильный ответ
5. Объяснение правильного ответа(по желанию).
Please open Telegram to view this post
    VIEW IN TELEGRAM
  👍8
  Что выведет следующий код?
  Anonymous Quiz
    19%
    Произойдет RuntimeException на строке 4
      
    3%
    Произойдет RuntimeException на строке 5
      
    14%
    Произойдет RuntimeException на строке 6
      
    35%
    nullnullnullnullnull
      
    29%
    Код не скомпилируется
      
    👍16🍾3😢2☃1
  Из чего состоит пакет java.nio?
Этому вопросу посвящена отдельная страница документации. Если вы никогда раньше не сталкивались с Java NIO – это хорошее место для начала знакомства. Отвечая на этот вопрос, нужно перечислить и объяснить основные понятия NIO:
Буфферы. Временные хранилища фиксированного размера для транспортируемых данных. Именно буферизация – основное отличие неблокирующего чтения от java.io.
Каналы. Реализации интерфейса Channel – сущности, представляющие соединения между разными участниками ввода-вывода (файлы, сокеты, консоль).
Селекторы. Наследники класса Selector. «Мультиплексоры» каналов – комбинируют несколько каналов в один. Регистрация канала в селекторе возвращает SelectionKey, который содержит ссылку на сам канал, и ряд его атрибутов. Селектор позволяет выбрать из набора зарегистрированных каналов подмножество готовых к работе, при необходимости блокируя выполнение на время ожидания. Каналы и селекторы располагаются в пакете java.nio.channels. Полный пример использования селекторов можно найти в статье на baeldung.
Кодировки. Charset – то, как бинарные данные будут конвертироваться в родные для Java символы UTF-16 и обратно. Классы для работы с кодировками хранятся в пакете java.nio.charset.
Этому вопросу посвящена отдельная страница документации. Если вы никогда раньше не сталкивались с Java NIO – это хорошее место для начала знакомства. Отвечая на этот вопрос, нужно перечислить и объяснить основные понятия NIO:
Буфферы. Временные хранилища фиксированного размера для транспортируемых данных. Именно буферизация – основное отличие неблокирующего чтения от java.io.
Каналы. Реализации интерфейса Channel – сущности, представляющие соединения между разными участниками ввода-вывода (файлы, сокеты, консоль).
Селекторы. Наследники класса Selector. «Мультиплексоры» каналов – комбинируют несколько каналов в один. Регистрация канала в селекторе возвращает SelectionKey, который содержит ссылку на сам канал, и ряд его атрибутов. Селектор позволяет выбрать из набора зарегистрированных каналов подмножество готовых к работе, при необходимости блокируя выполнение на время ожидания. Каналы и селекторы располагаются в пакете java.nio.channels. Полный пример использования селекторов можно найти в статье на baeldung.
Кодировки. Charset – то, как бинарные данные будут конвертироваться в родные для Java символы UTF-16 и обратно. Классы для работы с кодировками хранятся в пакете java.nio.charset.
👍13
  👍15🤯14🌭1
  Что такое ForkJoinPool?
ForkJoinPool – специальный вид ExecutorService (пулла потоков), который появился в Java с версии 7. Предназначен для выполнения рекурсивных задач.
Задача для сервиса представляется экземпляром класса ForkJoinTask. В основном используются подклассы RecursiveTask и RecursiveAction, для задач с результатом и без соответственно. Аналогично интерфейсам Callable и Runnable обычного ExecutorService.
Тело рекурсивной операции задается в реализации метода compute() задачи ForkJoinTask. Здесь же создаются новые подзадачи, и запускаются параллельно методом fork(). Чтобы дождаться завершения выполнения задачи, на каждой форкнутой подзадаче вызывается блокирующий метод join(), результат выполнения при необходимости агрегируется.
С точки зрения использования метод ForkJoinTask.join() похож на аналогичный метод класса Thread. Но в случае fork-join поток может на самом деле не заснуть, а переключиться на выполнение другой задачи. Такая стратегия называется work stealing, и позволяет эффективнее использовать ограниченное количество потоков. Это похоже на переиспользование потоков корутинах Kotlin (green threads).
Примеры практического использования ForkJoinPool.
ForkJoinPool – специальный вид ExecutorService (пулла потоков), который появился в Java с версии 7. Предназначен для выполнения рекурсивных задач.
Задача для сервиса представляется экземпляром класса ForkJoinTask. В основном используются подклассы RecursiveTask и RecursiveAction, для задач с результатом и без соответственно. Аналогично интерфейсам Callable и Runnable обычного ExecutorService.
Тело рекурсивной операции задается в реализации метода compute() задачи ForkJoinTask. Здесь же создаются новые подзадачи, и запускаются параллельно методом fork(). Чтобы дождаться завершения выполнения задачи, на каждой форкнутой подзадаче вызывается блокирующий метод join(), результат выполнения при необходимости агрегируется.
С точки зрения использования метод ForkJoinTask.join() похож на аналогичный метод класса Thread. Но в случае fork-join поток может на самом деле не заснуть, а переключиться на выполнение другой задачи. Такая стратегия называется work stealing, и позволяет эффективнее использовать ограниченное количество потоков. Это похоже на переиспользование потоков корутинах Kotlin (green threads).
Примеры практического использования ForkJoinPool.
👍20🔥1
  Что выведет следующий код?
  Anonymous Quiz
    62%
    It’s me!
      
    5%
    Выдаст IOException во время выполнения
      
    9%
    Выдаст Exception во время выполнения
      
    24%
    Код не скомпилируется
      
    👍13🤔9🍾1
  Чем ForkJoinPool отличается от ExecutorService?
ForkJoinPool сам по себе является наследником ExecutorService. Вопрос подразумевает его отличия от обычного пула потоков – ThreadPoolExecutor.
Преимущества, которые дает work stealing по сравнению с обычным пулом:
• Сокращение расходов на переключение контекста;
• Защита от проблемы голодания потоков (thread starvation);
• Защита от дедлока для рекурсивных задач.
Как положено любому представителю ExecutorService, ForkJoinPool тоже умеет выполнять Runnable и Callable, но помимо этого работает и со специальными задачами ForkJoinTask, о которых также говорилось ранее.
Интерфейс настройки и мониторинга остается тем же, что и в классических тред-пулах.
Каждый обычный пул использует собственный набор потоков. ForkJoinPool по умолчанию использует общий пул-синглтон commonPool. Альтернативный отдельный пул всё еще можно задать в конструкторе.
ForkJoinPool сам регулирует количество запущенных потоков, достигая максимальной эффективности при заданном уровне параллелизма.
ForkJoinPool сам по себе является наследником ExecutorService. Вопрос подразумевает его отличия от обычного пула потоков – ThreadPoolExecutor.
Преимущества, которые дает work stealing по сравнению с обычным пулом:
• Сокращение расходов на переключение контекста;
• Защита от проблемы голодания потоков (thread starvation);
• Защита от дедлока для рекурсивных задач.
Как положено любому представителю ExecutorService, ForkJoinPool тоже умеет выполнять Runnable и Callable, но помимо этого работает и со специальными задачами ForkJoinTask, о которых также говорилось ранее.
Интерфейс настройки и мониторинга остается тем же, что и в классических тред-пулах.
Каждый обычный пул использует собственный набор потоков. ForkJoinPool по умолчанию использует общий пул-синглтон commonPool. Альтернативный отдельный пул всё еще можно задать в конструкторе.
ForkJoinPool сам регулирует количество запущенных потоков, достигая максимальной эффективности при заданном уровне параллелизма.
👍19🌭1🍌1🍾1
  Что выведет следующий код?
  Anonymous Quiz
    28%
    012012012
      
    11%
    000111222
      
    2%
    010120212
      
    59%
    Все варианты правильные
      
    👍15☃11😁7🥴4🌭3