Java Interview Tasks
3.91K subscribers
185 photos
1 file
121 links
Реальные вопросы и задачи с собеседований.
Оригинальный авторский контент.
Актуальный материал.
Уровень вопросов от junior до supersenior.

Автор канала - @alexzelentsov

По рекламе: @alexzelentsov и https://telega.in/c/java_interview_tasks
Download Telegram
Какие значения может напечатать код (thread1 и thread2 запускаются одновременно в разных потоках)?
#jmm #concurrency #java #java_interview_tasks
👍11
Ответ на вопрос VolatileVsFinal (https://t.me/java_interview_tasks/140) :

Тут речь идет об одном из самых интересных разрешенных сценариев JMM. Это тот факт, что volatile поле не имеет final семантики.
Это означает, что если мы публикуем ссылку объекта под гонкой, то мы можем увидеть дефолтное значение для volatile поля
Поэтому в данном случае 0 вполне можно получить.

Этот эффект можно увидеть на некоторых платформах, например AArch64:
RESULT SAMPLES FREQ
-1 1,428,517,070 91.74%
0 7,105 <0.01%
42 128,534,641 8.25%
👍16🔥7🤯31
🔍 Ответ на задачу про то, как Java выбирает перегрузку метода?

🚀🚀🚀Когда вы вызываете метод в Java, который имеет несколько перегруженных вариантов, компилятор должен решить, какой из них использовать. Давайте разберёмся, как он это делает на примере вызова метода printFor(42).

Вот правила, которыми руководствуется компилятор:

🚀 Точный тип: Ищет метод с точным совпадением типа аргумента. У нас его нет для int, так как метода printFor(int) нет.

🚀 Примитивное расширение (widening): Далее проверяется, можно ли расширить примитивный тип. 42 (int) может быть преобразован в long, и метод printFor(long) подходит.

🚀 Автоупаковка (boxing): Если расширение невозможно, оценивается автоупаковка. int может стать Integer, что делает метод printFor(Integer) возможным, но менее предпочтительным, чем printFor(long).

🚀 Varargs (массив произвольной длины): Как запасной вариант, компилятор рассматривает использование varargs. Метод printFor(int...) подходит, но также менее предпочтителен.

🚀 Автоупаковка в Object: Ещё менее специфичный вариант — преобразование в Integer и затем в Object для printFor(Object).

🔗🔗 🔗 В итоге, компилятор выбирает printFor(long), потому что расширение примитивного типа более предпочтительно, чем автоупаковка и varargs. Это позволяет Java эффективно и предсказуемо обрабатывать вызовы методов!

🎓 Надеюсь, это было полезно! Оставьте свои вопросы, лайки и комментарии ниже. 👇

#Java #Программирование #CodingTips #JavaTips #java_interview_tasks
1👍23🔥53
🔍 Ответ на задачу про распределение монет по кошелькам
⚠️Это задача на метод stars&bars. если в условии есть упоминание, что кошельки не должны быть пустыми, то нам нужно заранее положить по одной монетке в каждый кошелек (иначе в ответе будут случаи, где некоторые кошельки пустые): 12 - 5 = 7
⚠️Теперь мы имеем 7 монет которые нужно распределить в 5 кошельков. Для этого возьмем 4 перегордки, (они поделят наши монетки на 5 частей("кошельков")). Выглядит пример так: 💰|💰💰|💰 |💰💰|💰

⚠️В общей сумме имеем 7+4 = 11 позиций, в котором нужно рассчитать количество перестановок перегородок, а это C(4 ; 11) = (11×10×9×8)/4×3×2×1 = 330
Ответ: 330 способов

🚀Если вам понравилось это объяснение, ставьте лайк, подписывайтесь на канал и делитесь своими мыслями в комментариях!
🚀🚀 Если хотите больше таких задач ставьте 🔥 к этому посту.

#Combinations #Combinatorics #MathChallenge #java_interview_tasks
👍12🔥32👎1
🔍 Ответ на задачу про то, как Java выбирает перегрузку метода с null?

В Java, когда вы вызываете перегруженный метод с аргументом null, компилятор должен решить, какую версию метода использовать. Давайте разберём конкретный пример с вызовом printFor(null).

👉 Как компилятор принимает решение?

👉👉Когда мы передаём null как аргумент, Java ищет наиболее специфичный метод, который может быть вызван. В этом конкретном случае, null может быть преобразован как в String, так и в Object, поскольку String является подтипом (наследником) Object.

👉👉Специфичность типа: Согласно правилам Java, если возможны несколько перегруженных методов, компилятор выбирает тот, чьё требование более специфично. Поскольку String является более специфичной версией Object, метод printFor(String o) будет выбран.

🔗 В итоге, когда вызывается printFor(null), на консоль будет выведено "String".

Такие правила выбора метода помогают Java обрабатывать вызовы предсказуемо и логично, даже с неопределёнными значениями, как null!

🎓 Надеюсь, это было полезно! Оставьте свои вопросы, лайки и комментарии ниже. 👇

#Java #Программирование #CodingTips #JavaTips #java_interview_tasks
🔥15👍611👏1🐳1
🔍 Ответ на задачу о ConcurrentHashMap и атомарности

Одно из часто обсуждаемых свойств ConcurrentHashMap — это ее потокобезопасность в отношении методов put и get. ConcurrentHashMap гарантирует атомарность операций put и get, что значит, что каждое из этих действий выполняется полностью или не выполняется вовсе, и они могут безопасно выполняться одновременно из разных потоков.

⚡️Однако важно помнить, что эта гарантия распространяется только на сами операции put и get, но не на атомарность действий по отношению к вложенным объектам. В нашем случае, если мы извлекаем список из мапы и затем изменяем его, эти изменения не защищены от состояния гонки. Как было показано в предыдущей задаче, два потока могут создать и модифицировать один и тот же список, приводя к непредсказуемым результатам.

⚡️⚡️Поэтому при работе с ConcurrentHashMap, когда объекты, хранящиеся в мапе, также должны быть потокобезопасными, следует дополнительно обеспечивать синхронизацию либо использовать другие потокобезопасные структуры данных.

👉 Чтобы решить проблему, можно воспользоваться несколькими подходами. Вот некоторые из них:

/*
Solves the race with putIfAbsent
*/

void addCorrect(String key, String val) {
List<String> list = map.get(key);
if (list == null) {
list = Collections.synchronizedList(new ArrayList<>());
List<String> exist = map.putIfAbsent(key, list);
if (exist != null) {
list = exist;
}
}
list.add(val);
}


/*
Solves the race with computeIfAbsent.
*/

 void addCorrect8(String key, String val) {
List<String> list = map.computeIfAbsent(key,
k -> Collections.synchronizedList(new ArrayList<>()));
list.add(val);
}


🎓 Понравилась тема про потокобезопасность в Java? Ставьте лайк, подписывайтесь на канал и делитесь своими мыслями в комментариях!

#ConcurrentHashMap #Java #Concurrency #Atomicity #java_interview_tasks
👍19🔥42👏1
🔍 Ответ на задачу про checkThenReact и состояние гонки:

В этом примере метода checkThenReact() описывается ситуация типичной проблемы конкурентного программирования, известной как "состояние гонки". Суть проблемы заключается в том, что несколько потоков (в данном случае actor1() и actor2()) могут одновременно проверить и изменить общее состояние, здесь представляемое переменной flag.

Возможные решения для устранения состояния гонки:
Синхронизация:

Заключите операцию проверки и изменения flag в блок синхронизации:
private synchronized boolean checkThenReact() {
if (flag) {
flag = false;
return true;
} else {
return false;
}
}

Использование AtomicBoolean:

Замена boolean на AtomicBoolean и использование атомарных операций. Например:

private final AtomicBoolean flag = new AtomicBoolean(true);

private boolean checkThenReact() {
return flag.compareAndSet(true, false);
}

Эти подходы помогут устранить логическую проблему и гарантировать, что только один поток сможет изменить состояние flag в одно и то же время.

🎓Если вам интересны темы конкурентности и потокобезопасности, ставьте лайк, подписывайтесь и оставляйте комментарии с вашими вопросами и переживаниями!

#Concurrency #RaceCondition #Synchronization #AtomicOperations #java_interview_tasks
👍16🔥31👌1
🔍 Ответ на задачу про JMM и особенности volatile полей

Данная задача подчеркивает одно из потенциально неожиданных поведений модели памяти Java (Java Memory Model - JMM), связанное с volatile полями. Конкретно, она касается разницы между volatile и final в контексте гарантий видимости и порядка инициализации.

⚠️ Ключевые моменты volatilе и final:

⚡️⚡️volatile гарантирует видимость изменений в переменной между потоками. Однако оно не гарантирует, что инициализация объекта до первой публикации будет выполнена полностью.
⚡️⚡️final гарантирует, что значение поля после его инициализации не изменяется, и другие потоки, получившие ссылку на объект, увидят окончательное (полностью инициализированное) состояние этого поля.
Проблема рваной публикации:

⚠️ Когда объект создается и его ссылка передается (опубликована) другим потокам, инициализация объекта может еще быть не завершена. В этом случае другие потоки могут видеть содержимое объекта в промежуточном состоянии. Это и есть суть "рваной" (racy) публикации.

Поведение volatile поля:

Если volatile поле объекта не было полностью инициализировано на момент, когда ссылка на объект становится видимой другим потокам, то впоследствии эти потоки могут наблюдать значение по умолчанию (например, 0 для примитивных типов).

⚠️ В коде выше делается следующее:

Объект Holder создается, и volatile поле x инициализируется в конструкторе.
Ссылка на объект публикуется в переменной h, которая не является volatile.
Второй поток считывает h и проверяет значение x.
Из-за отсутствия синхронизации (например, volatile для h), есть возможность, что thread2 увидит переменную h как не-null, но значение x еще не инициализированное, в результате чего оно может быть равно 0.

⚠️⚠️ Чтобы избежать подобных проблем:

Используйте final для полей, которые должны быть инициализированы полностью до публикации объекта. Это гарантирует, что другие потоки увидят полностью инициализированный объект.

Синхронизируйте публикацию объектов, чтобы гарантировать видимость полной инициализации.

Рассмотрите использование volatile для ссылочных переменных, через которые публикуются объекты, чтобы гарантировать полноценную инициализацию до публикации.

🎓 Если вы нашли это обсуждение полезным, ставьте лайк, подписывайтесь и делитесь своими мыслями в комментариях!

#JavaMemoryModel #VolatileFields #FinalFields #Synchronization #java_interview_tasks
🔥10👍41🐳1