Как пройти собеседование по Java: топ вопросов продвинутого уровня

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Как пройти собеседование по Java: топ вопросов продвинутого уровня

Собеседование на позицию Java-разработчика продвинутого уровня (Middle/Senior) — это не просто проверка синтаксиса. Интервьюеры хотят увидеть глубокое понимание внутреннего устройства JVM, многопоточности, алгоритмов работы сборщика мусора и нюансов стандартной библиотеки. В этой статье мы разберем самые частые и сложные вопросы, которые помогут вам уверенно пройти техническое интервью.

1. Многопоточность и синхронизация: от volatile до Lock

Продвинутое собеседование по Java почти всегда начинается с вопросов о многопоточности. Вам нужно не просто знать ключевое слово synchronized, но и понимать, как работают volatile, AtomicInteger, ReentrantLock и что такое happens-before.

Вопрос: В чем разница между synchronized и ReentrantLock?

Ключевое отличие — гибкость. synchronized — это блокировка на уровне монитора объекта. ReentrantLock предоставляет расширенные возможности: возможность прервать ожидание потока (lockInterruptibly()), попытку захвата без блокировки (tryLock()), а также поддержку нескольких условий (Condition).

// Пример использования ReentrantLock с Condition
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

public class BlockingQueueExample {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    private final Object[] items = new Object[10];
    private int putIndex, takeIndex, count;

    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) {
                notFull.await(); // ждем, пока не освободится место
            }
            items[putIndex] = x;
            if (++putIndex == items.length) putIndex = 0;
            count++;
            notEmpty.signal(); // сигналим, что элемент появился
        } finally {
            lock.unlock();
        }
    }

    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await(); // ждем, пока не появится элемент
            }
            Object x = items[takeIndex];
            if (++takeIndex == items.length) takeIndex = 0;
            count--;
            notFull.signal(); // сигналим, что место освободилось
            return x;
        } finally {
            lock.unlock();
        }
    }
}

Вопрос: Как работает ключевое слово volatile?

volatile гарантирует, что чтение и запись переменной происходит непосредственно из основной памяти (main memory), минуя кэш процессора. Это обеспечивает видимость изменений для всех потоков, но не гарантирует атомарность (например, инкремент count++ все равно требует синхронизации).

Также стоит помнить про happens-before: запись в volatile переменную происходит до любого последующего чтения этой же переменной из другого потока.

2. Java Memory Model (JMM) и Garbage Collector

Понимание того, как JVM управляет памятью, — обязательное требование для Senior-разработчика. Интервьюеры часто спрашивают про разные поколения (Young/Old Generation), типы сборщиков мусора и настройку JVM.

Вопрос: Какие типы Garbage Collector существуют в Java 17?

Современная Java (17+) поддерживает несколько GC:

  • G1GC (Garbage First) — GC по умолчанию. Разделяет кучу на регионы и собирает мусор в регионах с наибольшим количеством мертвых объектов.
  • ZGC (Z Garbage Collector) — низколатентный GC, работающий практически без пауз (менее 10 мс), даже для больших куч (до 16 ТБ).
  • Shenandoah — конкурентный GC с низким временем пауз, похож на ZGC, но реализован иначе.
  • Parallel GC — классический многопоточный GC, оптимизированный для пропускной способности.

Вопрос: Что такое Stop-The-World и как его избежать?

Stop-The-World (STW) — это момент, когда все потоки приложения приостанавливаются для выполнения GC. Избежать полностью нельзя, но можно минимизировать, используя низколатентные GC (ZGC, Shenandoah) или правильно настраивая размеры поколений:

// Пример настройки JVM для уменьшения пауз GC
// Использование ZGC:
// -XX:+UseZGC -Xms4g -Xmx4g -XX:ConcGCThreads=2

3. Коллекции: ConcurrentHashMap и WeakHashMap

Вопросы про коллекции на продвинутом уровне выходят за рамки "чем отличается ArrayList от LinkedList". Вас спросят про потокобезопасные коллекции и их внутреннее устройство.

Вопрос: Как работает ConcurrentHashMap?

В Java 8+ ConcurrentHashMap использует сегментирование с CAS-операциями (Compare-And-Swap) и синхронизацию только на уровне отдельных "корзин" (buckets) при коллизиях. Это позволяет нескольким потокам одновременно читать и писать данные без блокировки всей таблицы.

Ключевые особенности:

  • Чтение (get) — полностью без блокировок.
  • Запись (put) — использует synchronized только на конкретном узле списка/дерева.
  • Не допускает null ни для ключей, ни для значений.
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        
        // Безопасное добавление
        map.put("key1", 1);
        
        // Атомарная операция: если ключ отсутствует, вставить значение
        map.putIfAbsent("key2", 2);
        
        // Атомарное обновление
        map.compute("key1", (k, v) -> v == null ? 1 : v + 1);
        
        System.out.println(map.get("key1")); // 2
    }
}

Вопрос: Когда использовать WeakHashMap?

WeakHashMap использует слабые ссылки (WeakReference) для ключей. Если на ключ больше нет сильных ссылок в приложении, сборщи

Блоги

Рекомендаций по книгам