JavaRush /Java блог /Архив info.javarush /Перевод: Топ-50 интервью вопросов по нитям. Часть 2.
KapChook
19 уровень
Volga

Перевод: Топ-50 интервью вопросов по нитям. Часть 2.

Статья из группы Архив info.javarush
Вторая часть перевода оригинальной статьи Top 50 Java Thread Interview Questions Answers for Freshers, Experienced Programmers. Первая часть.
  1. Как проверить, удерживает ли нить lock?

  2. Я и не подозревал, что можно проверять, удерживает ли нить lock в данный момент, до тех пор, пока не столкнулся с этим вопросом в одном телефонном интервью. В java.lang.Thread есть метод holdsLock(), он возвращает true, тогда и только тогда, когда текущая нить удерживает монитор у определённого объекта.
  3. Как получить дамп нити?

  4. Дамп нити позволяет узнать, чем нить занимается в данный момент. Существует несколько способов получения дампа нити, зависящих от операционной системы. В Windows вы можете использовать комбинацию ctrl + Break, в Linux – команду kill -3. Также вы можете воспользоваться утилитой jstack, она оперирует над id процесса, который вы можете узнать с помощью другой утилиты jps.
  5. Какой JVM параметр используется для контроля размера стека нити?

  6. Это один из простых, -Xss параметер используется для контроля размера стека нити в Java.
  7. Различия между synchronized и ReentrantLock?

  8. Были времена, когда единственный способ достижения взаимного исключения был через ключевое слово synchronized, но он имеет несколько недостатков, например нельзя расширить lock за пределами метода или блока кода и т.д. Java 5 решает эту проблему, предоставляя более утончённый контроль через интерфейс Lock. ReentrantLock – распространённая реализация Lock, которая предоставляет Lock с таким же базовым поведением и семантикой, как у неявного монитора, достигаемый использованием синхронизированных методов, но с расширенными возможностями.
  9. Даны 3 нити Т1, Т2 и Т3? Как реализовать последовательность Т1, Т2, Т3?

  10. Последовательности можно достичь многими способами, но вы можете просто воспользоваться методом join(), чтобы запустить нить, когда другая закончит своё выполнение. Для реализации заданной последовательности, вам нужно запустить последнюю нить первой, и затем вызывать метод join() в обратном порядке, то есть Т3 вызывает Т2.join, а Т2 вызывает Т1.join, таким образом Т1 закончит выполнение первой, а Т3 последней.
  11. Что делает метод yield?

  12. Метод yield – один из способов попросить нить уступить процессор, чтобы другая могла выполниться. Это статический метод и он только гарантирует, что текущая нить уступит процессор, но не решает, к какой нити перейдёт исполнение.
  13. Какой уровень параллелизма у ConcurrentHashMap?

  14. ConcurrentHashMap достигает своей масштабируемости и ните-безопасности, разбивая действительную map на секции. Это разделение достигается использованием уровня параллелизма. Это опциональный параметр конструктора ConcurrentHashMap и его значение по-умолчанию 16.
  15. Что такое Semaphore?

  16. Semaphore – это новый тип синхронизатора. Это семафор со счётчиком. Концептуально, семафор управляет набором разрешений. Каждый acquire() блокируется, если необходимо, до того, как разрешение доступно, затем получает его. Каждый release() добавляет разрешение, потенциально освобождая блокирующий получатель (acquirer). Однако при этом не используются фактические объекты разрешений; Semaphore просто хранит количество доступных и действует соответственно. Semaphore используется для защиты дорогих ресурсов, которые доступны в ограниченном количестве, например подключение к базе данных в пуле.
  17. Что будет, если очередь пула нитей уже заполнена, а вы подадите задачу?

  18. Если очередь пула нитей заполнилась, то поданная задача будет «отклонена». Метод submit() у ThreadPoolExecutor'а выкидывает RejectedExecutionException, после чего вызывается RejectedExecutionHandler.
  19. Различия между методами submit() и execute() у пула нитей?

  20. Оба метода являются способами подачи задачи в пул нитей, но между ними есть небольшая разница. Execute(Runnable command) определён в интерфейсе Executor и выполняет поданную задачу в будущем, но, что более важно, ничего не возвращает. С другой стороны submit() – перегруженный метод, он может принимать задачи типов Runnable и Callable и может возвращать объект Future, который можно использовать для отмены выполнения и/или ожидания результата вычислений. Этот метод определён в интерфейсе ExecutorService, который наследуется от интерфейса Executor, и каждый класс пула нитей, например ThreadPoolExecutor или ScheduledThreadPoolExecutor, наследует эти методы.
  21. Что такое блокирующий метод?

  22. Блокирующий метод – метод, который блокируется, до тех пор, пока не выполнится задание, например метод accept() у ServerSocket блокируется в ожидании подключения клиента. Здесь блокирование означает, что контроль не вернётся к вызывающему методу до тех пор, пока не выполнится задание. С другой стороны, существуют асинхронные или не блокирующиеся методы, которые завершаются до выполнения задачи.
  23. Ните-безопасен ли Swing?

  24. Проще говоря, нет, Swing не ните-безопасен, но вам нужно объяснить, что вы этим имеете в виду, даже если интервьюер не спросил об этом. Когда мы говорим, что Swing не ните-безопасен, мы обычно ссылаемся на то, что это компонент, который не может быть модифицирован несколькими нитями. Все изменения GUI компонентов должны быть сделаны в AWT нити, и Swing предоставляет синхронные и асинхронные методы для планирования таких изменений.
  25. Различия между invokeAndWait и invokeLater?

  26. Это два метода Swing API, которые позволяют разработчикам обновлять GUI компоненты из нитей, а не из нити Диспетчера Событий. InvokeAndWait() синхронно обновляет GUI компонент, например полоска прогресса, каждый раз когда достигнут прогресс, полоска должна быть обновлена, чтоб отразить изменения. Если прогресс отслеживается в другой нити, он должен вызывать invokeAndWait() для назначения обновления этого компонента нитью Диспетчера Событий. А invokeLater() – асинхронный вызов обновления компонентов.
  27. Какие методы Swing API ните-безопасны?

  28. Этот вопрос снова о Swing и ните-безопасности, хоть компоненты Swing не ните-безопасны, существуют методы, которые можно безопасно вызвать из нескольких нитей. Я знаю, что repaint() и revalidate() ните-безопасны, но есть и другие методы на разных Swing компонентах, например setText() у JTextComponent, методы insert() и append() в классе JTextArea.
  29. Как создать неизменяемые объекты?

  30. Этот вопрос может показаться не имеющим ничего общего с многонитиевостью и параллелизмом, но он имеет. Неизменяемость помогает упростить и без того сложный параллельный код. Неизменяемый объект очень дорог для разработчиков, так как он может распространятся без какой-либо синхронизации. К сожалению в Java нет аннотации @Immutable, которая сделает ваш объект неизменяемым, для этого разработчикам нужно попотеть. Чтобы создать неизменяемый объект, нужно следовать основам: инициализация в конструкторе, отсутствие сеттеров, отсутствие утечки ссылки, хранить отдельно копии изменяемых объектов.
  31. Что такое ReadWriteLock?

  32. В целом, ReadWriteLock – это результат техники разбора lock'а для улучшения производительности параллельных приложений. Это интерфейс, который был добавлен в Java 5. Он оперирует парой связанных lock’ов, один для операций чтения, один для записи. Читающий lock может удерживаться одновременно несколькими читающими нитями, до тех пор пока не будет записывающих. Записывающий lock эксклюзивен. Если хотите, вы можете реализовать интерфейс с вашим набором правил, или вы можете использовать ReentrantReadWriteLock, который поддерживает максимум 65535 рекурсивных записывающих lock'ов и 65535 читающих lock'ов.
  33. Что такое busy spin?

  34. Busy Spin – это техника, которую программисты используют, чтобы заставить нить ожидать при определённом условии. В отличие от традиционных методов, wait(), sleep() или yield(), которые подразумевают уступание контроля над процессором, этот метод не уступает процессор, вместо этого он просто выполняет пустой цикл. Зачем кому-то делать это? Чтобы сохранить кэш процессора. В многоядерных системах, существует возможность, что приостановленная нить продолжит своё выполнение на другом ядре, что означает перестройку кэша. Чтобы избежать затратной перестройки, программист предпочитает ждать меньше, используя busy spin.
  35. Различия между volatile и atomic переменными?

  36. Это довольно интересный вопрос, сперва volatile и atomic переменные выглядят очень похоже, но всё же они разные. Volatile переменная предоставляет happens-before гарантию, что запись будет производится до любой последующей записи, она не гарантирует атомарность. Например, операция count++ не станет атомарной просто потому что count объявлена volatile. C другой стороны class AtomicInteger предоставляет атомарный метод для выполнения таких сложных операций атомарно, например getAndIncrement() – атомарная замена оператора инкремента, его можно использовать, чтобы атомарно увеличить текущее значение на один. Также существуют атомарные версии и для других типов данных.
  37. Что случится, если нить выбросит Исключение в синхронизированном блоке?

  38. Это ещё один вопрос с подвохом для обычных Java программистов. Не важно, как вы выходите из синхронизированного блока, нормально, закончив выполнение, или внезапно, выбросив исключение, нить освобождает полученный lock, при входе в синхронизированный блок. Это одна из причин, почему я предпочитаю синхронизированный блок lock интерфейсу, который требует особого внимания при освобождении lock'а, обычно это достигается освобождением lock'а в блоке finally.
  39. Что такое double checked locking Синглтона?

  40. Это один из самых популярных вопросов на интервью, и всё же, несмотря на его популярность, шансы, что кандидат ответит на него, в лучшем случае 50%. В половине случаев они проваливаются при написании кода, а в другой половине при объяснении, как это было сломано и пофикшено в Java 1.5. Это старый способ создания ните-безопасного синглтона, который пытается оптимизировать производительность, блокируясь только когда экземпляр синглтона создаётся впервые, но из-за сложности и факта, что он был сломан в JDK 1.4, мне лично он не нравится. Но всё-таки, даже если вы не предпочитаете этот подход, полезно его знать с точки зрения интервью.
  41. Как создать ните-безопасный Синглтон?

  42. Этот вопрос дополняет предыдущий. Если вы скажете, что не любите double checked locking, то интервьюер будет вынужден спросить об альтернативных способах создания ните-безопасного Синглтона. И они есть, вы можете воспользоваться особенностями загрузки классов и инициализации статических переменных для создания экземпляра Синглтона, или вы можете использовать преимущества мощного типа перечислений.
  43. Перечислите 3 обычая, которым вы следуете в параллельном программировании?

  44. Это мой любимый вопрос, потому что я считаю, что нужно соблюдать определённые правила при написании параллельного кода, что помогает в производительности, дебаге и поддержке. Далее представлены 3 лучших правила, которые, я считаю, должен соблюдать каждый Java программист:
    • Всегда давайте значимые имена своим нитям
    • Нахождение бага или отслеживание исключения в параллельном коде – довольно сложная задача. OrderProcessor, QuoteProcessor или TradeProcessor намного лучше, чем Thread-1. Thread-2 и Thread-3. Имя должно отражать задачу, выполняемую данной нитью. Все крупные фреймворки и даже JDK следуют этому правилу.
    • Избегайте блокировки или уменьшите масштабы синхронизации
    • Блокировка затратна, а переключение контекста ещё более затратно. Пытайтесь избегать синхронизации и блокировка насколько это возможно, и в необходимом минимуме вы уменьшите критическую секцию. Поэтому я предпочитаю синхронизированный блок синхронизированному методу, потому что это даёт вам абсолютный контроль над масштабом блокировки.
    • Между синхронизаторами и wait и notify, выбирайте синхронизаторы
    • Во-первых, синхронизаторы, типа CountDownLatch, Semaphore, CyclicBarrier или Exchanger упрощают кодинг. Очень сложно реализовывать сложный управляющий поток, используя wait и notify. Во-вторых, эти классы написаны и обслуживаются лучшими в бизнесе и есть большой шанс, что они будут оптимизированы или заменены более лучшим кодом в последующих релизах JDK. Используя утилиты синхронизации высокого уровня, вы автоматически получаете все эти преимущества.
    • Между Concurrent Collection и Synchronized Collection, выбирайте Concurrent Collection
    • Это ещё одно простое правило, которому легко следовать и пожинать выгоды. Concurrent колекции более масштабируемые, чем их синхронизрованые коллеги, поэтому при написании параллельного кода лучше использовать их. Так что в следующий раз, когда вам понадобится map, подумайте о ConcurrentHashMap прежде, чем подумаете о Hashtable.
  45. Как вынужденно запустить нить?

  46. Это вопрос а ля как вынужденно запустить сборку мусора. Коротко, никак, вы, конечно, можете сделать запрос, используя System.gc(), но он ничего не гарантирует. В Java нет абсолютно никакого способа вынужденного запуска нити, это контролируется планировщиком нитей и Java не предоставляет никакой API для его контроля. Это часть Java всё ещё рандомна.
  47. Что такое фреймворк Fork/Join?

  48. Фреймворк Fork/Join, представленный в JDK 7, - это мощная утилита, позволяющая разработчику пользоваться преимуществами нескольких процессоров у современных серверов. Он разработан для работы, которую можно рекурсивно разбить на маленькие частицы. Цель – использовать всю доступную вычислительную мощь, для увеличения производительности вашего приложения. Одного значительное преимущество этого фреймворка в том, что он использует work-stealing алгоритм (от work – работа и steal – красть). Рабочие нити, у которых закончились свои задания, могут «своровать» задания у других нитей, которые всё ещё заняты.
  49. В чём разница вызовов методов wait() и sleep()?

  50. Хотя и wait, и sleep представляют подобие паузы в Java приложении, это устройства для различных нужд. Wait используется для внутренней коммуникации нитей, он уступает lock, если условие ожидание истинно, и ждёт уведомления, если из-за действий другой нити условие ожидания ложно. С другой стороны метод sleep() просто уступает процессор или останавливает выполнение текущей нити на заданное время. Вызов sleep() не освобождает удерживаемый текущей нитью lock.
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
hidden #819051 Уровень 34
7 августа 2018
Concurrent hashmap блокирует по бакетам, по умолчанию бакетов 16.