Логотип YeaHub

База вопросов

Собеседования

Тренажёр

База ресурсов

Обучение

Навыки

Войти

Выбери, каким будет IT завтра — вместе c нами!

YeaHub — это полностью открытый проект, призванный объединить и улучшить IT-сферу. Наш исходный код доступен для просмотра на GitHub. Дизайн проекта также открыт для ознакомления в Figma.

© 2026 YeaHub

Документы

Медиа

Назад
Вопрос про Java: race condition, thread safety, mutex, atomic operations, synchronization

Как исправить проблему гонки потоков в счетчике?

Вопрос проверяет понимание проблемы состояния гонки при работе с общими ресурсами в многопоточном программировании и способы её решения.

Короткий ответ

Проблема гонки потоков возникает, когда несколько потоков одновременно читают и изменяют общую переменную, например, счётчик, без синхронизации. Это может привести к потере обновлений и некорректному итоговому значению. Для исправления нужно обеспечить атомарность операций над счётчиком, используя мьютексы для блокировки доступа или атомарные типы данных, которые гарантируют выполнение операций чтения-модификации-записи как единого целого.

Длинный ответ

Проблема гонки потоков (race condition) — классическая проблема многопоточного программирования, возникающая, когда несколько потоков одновременно обращаются к общему ресурсу (например, переменной-счётчику) и хотя бы один из потоков выполняет запись. Без синхронизации операции чтения и записи могут "перекрываться", приводя к потере данных и недетерминированному результату.

Почему возникает проблема?

Рассмотрим простой счётчик. Операция инкремента counter++ на уровне процессора обычно состоит из трёх шагов: чтение значения из памяти, увеличение его на единицу, запись результата обратно. Если два потока выполняют эту операцию одновременно, они могут прочитать одно и то же исходное значение, независимо увеличить его и записать одинаковый результат, что приведёт к потере одного из инкрементов.

Основные способы решения

  • Мьютексы (Mutex): Обеспечивают эксклюзивный доступ к критической секции кода. Перед изменением счётчика поток захватывает мьютекс, а после завершения — освобождает.
  • Атомарные операции: Специальные типы данных (например, std::atomic в C++ или AtomicInteger в Java), которые гарантируют, что операции над ними выполняются как единое целое, без вмешательства других потоков.
  • Синхронизированные блоки/методы: В языках вроде Java можно использовать ключевое слово synchronized для методов или блоков кода.

Пример кода на C++

Демонстрация проблемы и её решения с помощью мьютекса и атомарного типа.

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <atomic>

// Глобальный счётчик с проблемой гонки
int unsafe_counter = 0;

// Счётчик, защищённый мьютексом
int mutex_counter = 0;
std::mutex mtx;

// Атомарный счётчик
std::atomic<int> atomic_counter(0);

void increment_unsafe() {
    for (int i = 0; i < 10000; ++i) {
        unsafe_counter++; // Потенциальная гонка!
    }
}

void increment_with_mutex() {
    for (int i = 0; i < 10000; ++i) {
        std::lock_guard<std::mutex> lock(mtx);
        mutex_counter++;
    }
}

void increment_atomic() {
    for (int i = 0; i < 10000; ++i) {
        atomic_counter++; // Атомарная операция
    }
}

int main() {
    std::vector<std::thread> threads;
    // Запуск потоков для каждого метода
    // ... (код создания и запуска потоков)
    // После join выведем значения
    std::cout << "Unsafe: " << unsafe_counter << std::endl;
    std::cout << "Mutex: " << mutex_counter << std::endl;
    std::cout << "Atomic: " << atomic_counter << std::endl;
    return 0;
}

В этом примере unsafe_counter почти наверняка покажет значение меньше ожидаемого (например, 20000 при двух потоках), в то время как mutex_counter и atomic_counter будут корректными.

Где применяется

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

Вывод: Для исправления гонки в счётчике используйте атомарные операции для простых числовых типов, так как они обычно эффективнее мьютексов. Мьютексы же предпочтительнее для защиты более сложных структур данных или когда критическая секция содержит несколько операций, которые должны выполняться атомарно вместе.

Уровень

  • Рейтинг:

    4

  • Сложность:

    5

Навыки

  • Java

    Java

  • C

    C

Ключевые слова

#race condition

#thread safety

#mutex

#atomic operations

#synchronization

Подпишись на Java Developer в телеграм