Вопрос проверяет понимание классических проблем многопоточности, таких как race condition и thread interference, которые возникают при несинхронизированном доступе к общим данным.
При разработке многопоточных приложений, когда несколько потоков выполняются параллельно и обращаются к общим данным, возникают фундаментальные проблемы корректности. Две ключевые из них — race condition (состояние гонки) и thread interference (взаимодействие потоков).
Состояние гонки — это ситуация, когда поведение программы зависит от относительного порядка выполнения потоков, который непредсказуем и может меняться от запуска к запуску. Это происходит, когда несколько потоков одновременно читают и записывают одну и ту же переменную или структуру данных, и хотя бы одна из операций является записью. Результат зависит от того, какой поток "выиграл гонку" и выполнил свою операцию первым.
Thread interference — это более конкретное проявление race condition, когда последовательность операций, выполняемых разными потоками над общими данными, переплетается (interleaves). Например, простая операция инкремента counter++ на самом деле состоит из трёх шагов: 1) чтение значения, 2) увеличение на 1, 3) запись нового значения. Если два потока выполняют эту операцию одновременно, их шаги могут перемешаться, что приведёт к потере одного из обновлений.
Рассмотрим класс с общей переменной, которую инкрементируют несколько потоков без синхронизации:
public class UnsafeCounter {
private int count = 0;
public void increment() {
count++; // Неатомарная операция!
}
public int getCount() {
return count;
}
public static void main(String[] args) throws InterruptedException {
UnsafeCounter counter = new UnsafeCounter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
t1.start();
t2.start();
t1.join();
t2.join();
// Ожидаем 2000, но часто получаем меньше (например, 1998)
System.out.println("Final count: " + counter.getCount());
}
}В этом коде два потока выполняют по 1000 инкрементов. Из-за race condition итоговое значение часто будет меньше 2000, так как некоторые инкременты перезапишут результаты друг друга.
Для предотвращения race condition и thread interference используются механизмы синхронизации:
Вывод: Проблемы race condition и thread interference — это критически важные аспекты многопоточного программирования, которые необходимо учитывать при проектировании систем, работающих с общими данными. Для их решения следует применять синхронизацию, атомарные типы или проектировать приложение так, чтобы минимизировать разделяемое изменяемое состояние.