🏎 Состояние гонки (Race Condition) — это ситуация, когда несколько потоков одновременно получают доступ к общему ресурсу без должной синхронизации. В результате поведение программы становится непредсказуемым, так как порядок выполнения потоков может влиять на конечный результат.
Пример:
public class RaceConditionStringBuilder {
public static void main(String[] args) throws InterruptedException {
StringBuilder sb = new StringBuilder(); // Общий ресурс
Runnable task = () -> {
for (int i = 0; i < 10000; i++) {
sb.append("A"); // Несинхронизированный доступ
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Result length: " + sb.length());
}
}👀 Что происходит?
▪️ Два потока одновременно добавляют символ "A" в один и тот же StringBuilder объект. Поскольку StringBuilder не является потокобезопасным, возможны ошибки, такие как потеря данных или некорректные результаты.
▪️ Потоки не знают о действиях друг друга. Если два потока одновременно читают строку длиной 500, каждый добавляет свой символ "A", а затем оба записывают новую строку с длиной 501, одно добавление теряется. Это и есть состояние гонки — результат зависит от того, как потоки "успеют" выполнить свои операции.
▪️ Ожидаемая длина строки — 20000, но на практике результат может быть меньше.
🛠 Как исправить?
🔵 Использовать StringBuffer, который синхронизирован и потокобезопасен.
🔵 Синхронизировать доступ к StringBuilder вручную:
synchronized(sb) {
sb.append("A");
}