JavaRush /Java Blog /Random-IT /Traduzione: le 50 principali domande dell'intervista per ...
KapChook
Livello 19
Volga

Traduzione: le 50 principali domande dell'intervista per thread. Parte 1.

Pubblicato nel gruppo Random-IT
La prima parte della traduzione dell'articolo originale Le 50 principali domande di intervista sui thread Java Risposte per principianti ed esperti programmatori. Seconda parte. Nota: l'articolo si è rivelato ampio, motivo per cui non rientra in un argomento. Inoltre, è piuttosto complicato, ho fatto del mio meglio per cercarlo su Google, ma comunque. Chiediamo quindi ai partecipanti che parlano correntemente l'inglese di dare un'occhiata all'originale e di confrontarlo con la traduzione, nel caso abbiano frainteso qualcosa o lo abbiano tradotto in modo errato. Grazie in anticipo. In qualsiasi colloquio, senior o junior, esperto o principiante, ti troverai di fronte ad un paio di domande su thread, parallelismo e multi-threading. In effetti, questo supporto integrato per la concorrenza è uno dei maggiori punti di forza di Java e lo ha aiutato a raggiungere la popolarità sia tra gli imprenditori che tra i programmatori. Le posizioni di sviluppatore Java più redditizie richiedono eccellenti competenze multi-threading ed esperienza nello sviluppo, nel debug e nella messa a punto di applicazioni ad alte prestazioni e a bassa latenza. Pertanto, è una delle competenze più ricercate nelle interviste. In una tipica intervista Java, l'intervistatore inizia lentamente con i concetti di base dei thread, ponendo domande come perché sono necessari i thread, come crearli, quale modo di crearli è migliore, ereditando da Thread o implementando Runnable, e poi si muove lentamente passando alle difficoltà di concorrenza, alle difficoltà incontrate nello sviluppo di applicazioni parallele, alle utilità di concorrenza di alto livello introdotte in JDK 1.5, ai principi e ai modelli di progettazione delle applicazioni parallele e ai classici problemi di multithreading. Poiché non è sufficiente conoscere le nozioni di base del multithreading, è necessario sapere come gestire problemi di concorrenza come deadlock, condizioni di competizione, incoerenza della memoria e vari problemi di sicurezza dei thread. Queste competenze vengono accuratamente testate, presentando varie sfide multi-threading e concorrenza. Molti sviluppatori Java di solito leggono le domande prima del colloquio stesso, il che non è una cosa negativa, ma dovresti capirlo. Inoltre, accumulare domande e fare gli stessi esercizi fa perdere molto tempo, quindi ho creato questo elenco.
  1. Cos'è un thread in Java?

  2. Un thread è un percorso di esecuzione indipendente. Il suo obiettivo è sfruttare i molteplici processori disponibili nella macchina. Utilizzando più thread è possibile velocizzare le attività legate alla CPU. Ad esempio, se un thread impiega 100 millisecondi per completare un lavoro, è possibile utilizzare 10 thread per ridurre il lavoro a 10 millisecondi. Java fornisce un eccellente supporto per il multi-threading a livello di linguaggio e questo, del resto, è uno dei suoi maggiori vantaggi.
  3. Differenza tra thread e processi in Java?

  4. Нить – это подмножество процесса, другими словами один процесс может содержать множество нитей. Два процесса исполняются на различных пространствах памяти, но все нити делят одно пространство. Не спутайте это с памятью стека, которая различная для каждой нити и используется для хранения локальных данных этой нити.
  5. Как создать нить?

  6. На уровне языка есть два способа создания нити. Объект класса java.lang.Thread представляет собой нить, но ей требуется задача для исполнения, которая является an objectом, реализующим интерфейс java.lang.Runnable. Так How класс Thread реализует интерфейс Runnable, вы можете переопределить метод run() унаследовав ваш класс от Thread or реализовав в нём интерфейс Runnable.
  7. Когда использовать Runnable и когда Thread?

  8. Это дополнение к предудыщему вопросу. Как мы знаем, нить можно создать унаследовавшись от класса Thread or реализовав интерфейс Runnable. Возникает вопрос, который из способов лучше и когда Howой использовать? На этот вопрос легко ответь, если вы знаете, что Java не поддерживает множественное наследование классов, но позволяет реализовывать множество интерфейсов. What означает, что лучше реализовывать Runnable, если вы хотите унаследоваться от другого класса.
  9. Difference между методами start() и run()?

  10. Один из вопросов с подвохом из прошлого, но он всё ещё достаточно хорош, чтобы отличить поверхностное понимание многонитиевости в Java. Метод start() используется для запуска новой нити. Несмотря на то, что start() вызывает метод run() внутри себя, это не то же самое, что просто вызвать run(). Если вы вызываете run() How обычный метод, он вызывается в той же нити и ниHowая новая нить не запуститься, что происходит, когда вы вызываете метод start().
  11. Различия Runnable и Callable?

  12. Оба интерфейса представляют задачи, которые предназначены для выполнения в отдельных нитях. Runnable существует ещё с JDK 1.0, а Callable был добавлен в JDK 1.5. Главное их различие заключается в том, что метод call() у Callable может возвращать значения и выбрасывать исключения, что невозможно в методе run() у Runnable. Callable возвращает an object Future, который может содержать результат вычислений.
  13. Различия между CyclicBarrier и CountDownLatch?

  14. Хоть оба эти синхронизаторы позволяют нитям дожидаться друг друга, главное различие между ними в том, что вы не можете заново использовать CountDownLatch после того, How его счётчик достигнет нуля, но вы можете использовать CyclicBarrier снова, даже после того, How барьер сломается.
  15. What такое модель памяти Java?

  16. Модель памяти – это набор правил и указаний, которые позволяют Java программам действовать детерминировано среди множества архитектур памяти, процессора и операционной системы. Это особенно важно в случае многонитиевости. Модель памяти предоставляет гарантии того, что изменения, произведённые одной нитью, будут видны для других, одна из них – отношение happens-before (случалось ранее). Это отношение определяет несколько правил, которые позволяют программистам предвидеть и определять поведение параллельных программ. Например happens-before гарантирует:
    • Каждое действие в нити случается раньше любого действия в этой нити, которое следует в программном порядке, это также известно, How правило программного порядка.
    • Разблокировка монитора случается раньше каждой последующей блокировки того же самого монитора, также известно, How правило блокировки Монитора.
    • Запись volatile поля случается раньше каждого последующего чтения этого поля, правило изменчивой переменной.
    • Вызов Thread.start() в нити случается раньше, чем любая другая нить замечает, что нить была остановлена, либо после удачного Thread.join(), либо если Thread.isAlive() возвращает false, правило Thread.start().
    • Прерывание нити из другой нити случается раньше, чем прерванная нить заметит прерывание (либо от выброса InterruptedException, либо от проверки isInterrupted()), правило прерывания нити.
    • Окончание конструктора an object случается раньше, чем запуск финализатора для этого an object, правило Финализатора.
    • Если А случается раньше В, и В случается раньше С, значит А случается раньше С, что значит happens-before гарантирует транзитивность.
  17. What такое volatile переменная?

  18. Volatile – специальный модификатор, который может применяться только к атрибутам. В параллельных Java программах изменения, произведённые различными нитями на атрибутах, не видны для остальных при отсутствии синхронизатора. Volatile переменная гарантирует, что запись будет осуществляться до последующего чтения, что сказано в правиле изменчивой переменной в предыдущем вопросе.
  19. What такое ните-безопасность? Безопасный ли класс Vector?

  20. Ните-безопасность – свойвство an object or codeа, которое гарантирует, что при исполнении or использовании несколькими нитями, code будет вести себя, How предполагается. Например ните-безопасный счётчик не пропустит ни один счёт, если тот же экземпляр счётчика используется среди нескольких нитей. Очевидно, можно разделить классы коллекций на две категории, ните-безопасные и не-ните-безопасные. Vector ните-безопасен и достигает этого синхронизацией методов, которые изменяют состояние Vector’a, с другой стороны его коллега ArrayList не-ните-безопасен.
  21. What такое состояние гонки (race condition)?

  22. Состояние гонки – причина трудноуловимых багов. Как сказано в самом названии, состояние гонки возникает из-за гонки между несколькими нитями, если нить, которая должна исполнятся первой, проиграла гонку и исполняется вторая, поведение codeа изменяется, из-за чего возникают недетерменированные баги. Это одни из сложнейших к отлавливанию и воспроизведению багов, из-за беспорядочной природы гонок между нитями. Пример состояния гонки – беспорядочное исполнение.
  23. Как остановить нить?

  24. Я всегда говорил, что Java предоставляет богатые API для всего, но, по иронии судьбы, не предоставляет удобных способов остановки нити. В JDK 1.0 было несколько управляющих методов, например stop(), suspend() и resume(), которые были помечены How deprecated в будущих релизах из-за потенциальных угроз взаимной блокировки, с тех пор разработчики Java API не предприняли попыток представить стойкий, ните-безопасный и элегантный способ остановки нитей. Программисты в основном полагаются на факт того, что нить останавливается сама, How только заканчивает выполнять методы run() or call(). Для остановки вручную, программисты пользуются преимуществом volatile boolean переменной и проверяют её meaning в каждой итерации, если в методе run() есть циклы, or прерывают нити методом interrupt() для внезапной отмены заданий.
  25. What происходит, когда в нити появляется исключение?

  26. Это один из хороших вопросов с подвохом. Простыми словами, если исключение не поймано – нить мерта, если установлен обработчик непойманных исключений, он получит колбек. Thread.UncaughtExceptionHandler – интерфейс, определённый How вложенный интерфейс для обработчиков, вызываемых, когда нить внезапно останавливается из-за непойманного исключения. Когда нить собирается остановится из-за непойманного исключения, JVM проверит её на наличие UncaughtExceptionHandler, используя Thread.getUncaughtExceptionHandler(), и вызовет у обработчика метод uncaughtException(), передав нить и исключение в виде аргументов.
  27. Как поделиться данными между двумя нитями?

  28. Вы можете делиться данным между нитями, используя общий an object or параллельные структуры данных, типа BlockingQueue.
  29. Различия между notify и notifyAll?

  30. Это ещё один из вопросов с подвохом, так How за одним монитором могут наблюдать несколько нитей, Java API разработчики предоставляют метод для уведомления об изменении его состояния только одной or сразу всех нитей, но они предоставляют только половину реализации. У метода notify() не реализован способ выбора определённой нити, поэтому он полезен только когда вы точно знаете, что всего одна нить ожидает. С другой стороны, notifyAll() уведомляет все нити и позволяет им побороться за монитор, что гарантирует, что по крайней мере одна нить продвинется дальше.
  31. Почему wait, notify и notifyAll не в классе Thread?

  32. Это вопрос, относящийся к дизайну, который проверяет, что кандидат думает о существующих системах or думал ли он когда-либо о чём-то схожем, но выглядящем неуместно поначалу. Whatбы ответить на этот вопрос, вам нужно предоставить несколько причин, почему эти методы удобнее реализовывать в классе Object, и почему не в классе Thread. Первая очевидная причина – Java поддерживает lock на уровне an objectов, а не на уровне нитей. Любой an object имеет lock, который получает нить. И если нити нужно ждать определённый lock, есть смысл в том, чтобы вызвать wait() на an object, чем на эту нить. Если бы wait() был объявлен в классе Thread, было бы не ясно, Howой lock нить ждёт. Вкратце, так How wait, notify и notifyAll работают на уровне lock, удобнее объявить их в классе Object, потому что lock относится к an objectу.
  33. What такое ThreadLocal переменная?

  34. ThreadLocal переменные – специальный вид переменных, доступных Java программисту. Так же, How для состояний есть переменная состояния, для нитей есть ThreadLocal переменные. Это неплохой способ достичь ните-безопасности для затратных-для-создания an objectов, например вы можете сделать SimpleDateFormat ните-безопасным, используя ThreadLocal. Так How это затратный класс, его нежелательно использовать в локальной области, которая требует отдельных экземпляров на каждый вызов. Предоставляя каждой нити её собственную копию, вы убиваете двух зайцев. Во-первых, вы уменьшаете количество экземпляров затратных an objectов, используя по новой фиксированное количество экземпляров, и во-вторых, вы достигаете ните-безопасности, без потерь синхронизации и неизменяемости. Ещё один хороший пример локальной переменной у нити – класс ThreadLocalRandom, который уменьшает количество экземпляров затратных-для-создания an objectов Random в много-нитиевой среде.
  35. What такое FutureTask?

  36. FutureTask представляет собой отменяемое асинхронное вычисление в параллельном Java приложении. Этот класс предоставляет базовую реализацию Future, с методами для запуска и остановки вычисления, методами для requestа состояния вычисления и извлечения результатов. Результат может быть получен только когда вычисление завершено, метод получения будет заблокирован, если вычисление ещё не завершено. Объекты FutureTask могут быть использованы для обёртки an objectов Callable и Runnable. Так How FutureTask реализует Runnable, его можно передать Executor’у на выполнение.
  37. Различие между interrupted и isInterrupted?

  38. Основное различие между interrupted() и isInterrupted() в том, что первый сбрасывает статус прерывания, а второй нет. Механизм прерывания в Java реализован с использованием внутреннего флага, известного How статус прерывания. Прерывание нити вызовом Thread.interrupt() устанавливает этот флаг. Когда прерванная нить проверяет статус прерывания, вызывая статический метод Thread.interrupted(), статус прерывания сбрасывается. Нестатический метод isInterrupted(), который используется нитью для проверки статуса прерывания у другой нити, не изменяет флаг прерывания. Условно, любой метод, который завершается, выкинув InterruptedException сбрасывает при этом флаг прерывания. Однако, всегда существует возможность того, что флаг тут же снова установится, если другая нить вызовет interrupt().
  39. Почему методы wait и notify вызываются в синхронизированном блоке?

  40. Основная причина вызова wait и notify из статического блока or метода в том, что Java API обязательно требует этого. Если вы вызовете их не из синхронизированного блока, ваш code выбросит IllegalMonitorStateException. Более хитрая причина в том, чтобы избежать состояния гонки между вызовами wait и notify.
  41. Почему вы должны проверять состояние ожидания в цикле?

  42. Существует возможность того, что ожидающая нить получит ложные предупреждения и ложные вызовы пробуждения, если она не проверит состояние ожидания в цикле, она просто выйдет, даже если состояние не достигнуто. Когда ожидающая нить пробуждается, она не думает о том, что состояние, которое она ожидала, может все ещё оставаться в силе. Оно могло быть действительно в прошлом, но потом быть изменено после вызова метода notify() и перед тем How нить пробудилась. Поэтому всегда лучше вызывать wait() из цикла.
  43. Различия между synchronized и concurrent коллекциями?

  44. Хоть обе synchronized и concurrent коллекции предоставляют ните-безопасные коллекции, последняя является более масштабируемой. До Java 1.5 программистам были доступны только synchronized коллекции, которые становorсь источником раздора, когда несколько нитей обращались к ним одновременно, что затрудняло масштабирование системы. Java 5 представила concurrent коллекции, например ConcurrentHashMap, которые не только предоставляют ните-безопасность, но также улучшают масштабируемость, используя современные техники, такие How lock stripping и partitioning internal table.
  45. Различия между Стеком и Кучей?

  46. Почему этот вопрос присутствует в вопросах о много-нитиевости? Потому что стек – участок памяти, тесно связанный с нитями. У каждой нити есть свой стек, которые хранит локальные переменные, параметры методов и стек вызовов. Переменная, хранящаяся в стеке одной нити, не видна для другой. С другой стороны, куча – общий участок памяти, который делится между всеми нитями. Объекты, неважно локальные or любого другого уровня, создаются в куче. Для улучшения производительности, нить обычно кэширует значения из кучи в свой стек, тут-то выползают volatile переменные. Volatile указывает нитям на то, что переменную нужно читать из главной памяти.
  47. What такое пул нитей?

  48. Creation нити затратно в плане времени и ресурсов. Если вы создаёте нить во время обработки requestа, это замедлит время отклика, также процесс может создать только ограниченное число нитей. Whatбы избежать этих проблем, во время запуска applications создаётся пул нитей и нити повторно используются для обработки requestов. Этот пул нитей называется «thread pool», а нити в нём – рабочая нить. Начиная с Java 1.5 Java API предоставляет фреймворк Executor, который позволяет вам создавать различные пулы нитей, например single thread pool, который обрабатывает только одно задание за единицу времени, fixed thread pool, пул с фиксированным количеством нитей, и cached thread pool, расширяемый пул, подходящий для приложений с множеством недолгих заданий.
  49. Как решить Producer Consumer проблему?

  50. Большинство нитиевых проблем, которые вы решаете в реальности, из категории паттерна Producer Consumer, в котором одна нить порождает задачу, а вторая поглощает её. Вам нужно знать, How построить внутренние взаимодействия нитей, для решения этой проблемы. На низком уровне вы можете воспользоваться методами wait и notify, а на высоком уровне вы можете воспользоваться преимуществами Semaphore or BlockingQueue
  51. Как избежать взаимной блокировки (deadlock)?

  52. Traduzione: le 50 principali domande dell'intervista per thread.  Parte 1. - 1 Deadlock – состояние, в котором нить ждёт, пока вторая нить совершит Howое-либо действие, а вторая, в это же время, ждёт того же от первой. Это очень серьёзная проблема, из-за которой ваша программа зависает и не делает того, для чего она предназначена. Deadlock происходит, когда достигаются эти 4 состояния:
    • Взаимное исключение: по крайней мере один ресурс должен быть занят в режиме неделимости. Только один процесс может использовать ресурс в любой данный момент времени.
    • Удержание и ожидание: процесс удерживает How минимум один ресурс и запрашивает дополнительных ресурсов, которые удерживаются другими процессами.
    • Нет пред-очистке: операционная система не переназначивает ресурсы, если они уже заняты, они должны отдаваться удерживающим процессом добровольно.
    • Цикличное ожидание: процесс ждёт освобождения ресурсов другим процессом, который в свою очередь ждёт освобождения ресурсов первым процессом.
    Il modo più semplice per evitare un deadlock è evitare l'attesa in un ciclo; ciò può essere ottenuto acquisendo i lock in un determinato ordine e rilasciandoli nell'ordine inverso.
  53. Differenze tra livelock e deadlock?

  54. Un livelock è simile a un deadlock, solo che in un livelock gli stati dei thread o dei processi coinvolti cambiano costantemente l'uno rispetto all'altro. Livelock è un caso speciale di carenza di risorse. Un vero esempio di livelock è quando due persone si incontrano in uno stretto corridoio e ciascuna, cercando di essere educata, si fa da parte, e così si muovono all'infinito da una parte all'altra.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION