← Назад к вопросам

В чем разница между потоками и корутинами?

2.2 Middle🔥 293 комментариев
#Многопоточность и асинхронность

Комментарии (3)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Основное различие: потоки vs корутины

Потоки (threads) — это низкоуровневые механизмы многозадачности, предоставляемые операционной системой. Каждый поток имеет собственный стек, планируется системным планировщиком и работает параллельно (или псевдопараллельно на одноядерных системах).

Корутины (coroutines) — это легковесные конструкции для асинхронного программирования, реализованные на уровне языка или библиотеки. Они не привязаны к конкретным потокам и могут приостанавливать выполнение без блокировки потока.

Ключевые отличия

1. Стоимость создания и переключения контекста

// Создание потока (дорого - ~1MB стека)
Thread {
    println("Thread running")
}.start()

// Создание корутины (дешево - несколько KB)
GlobalScope.launch {
    println("Coroutine running")
}

2. Управление состоянием

  • Потоки управляются ОС, переключение между ними требует сохранения/восстановления полного контекста процессора
  • Корутины управляются программистом или библиотекой, переключение происходит только в точках suspension

3. Параллелизм vs кооперативная многозадачность

// Потоки - вытесняющая многозадачность
val thread1 = Thread { heavyTask1() }
val thread2 = Thread { heavyTask2() }
// ОС сама решает, когда переключаться между потоками

// Корутины - кооперативная многозадачность
suspend fun task1() {
    // Явно освобождаем поток для других корутин
    delay(100)
    heavyWork1()
    yield() // Можем добровольно уступить выполнение
}

4. Блокирующие vs неблокирующие операции

// Поток блокируется на время операции
Thread.sleep(1000) // Поток НЕ может делать другую работу

// Корутина приостанавливается без блокировки потока
delay(1000) // Поток МОЖЕТ выполнять другие корутины

5. Масштабирование

На одном потоке можно запускать тысячи корутин, в то время как создание тысяч потоков приведет к исчерпанию ресурсов:

// Проблематично с потоками
for (i in 1..10000) {
    Thread { /* работа */ }.start() // Скорее всего OutOfMemoryError
}

// Легко с корутинами
for (i in 1..10000) {
    launch { /* работа */ } // Отлично работает
}

Практические преимущества корутин в Android

Упрощение асинхронного кода

// Старый подход с колбэками (Callback Hell)
api.getUser { user ->
    api.getPosts(user.id) { posts ->
        api.getComments(posts.first().id) { comments ->
            // Обработка
        }
    }
}

// С корутинами (последовательный код)
suspend fun loadUserData() {
    val user = api.getUser() // Приостанавливаем, но не блокируем
    val posts = api.getPosts(user.id)
    val comments = api.getComments(posts.first().id)
    // Чистый, линейный код
}

Контроль отмены и тайм-аутов

// Легко отменяем операции
val job = launch {
    try {
        longRunningTask()
    } finally {
        // Cleanup ресурсов при отмене
    }
}

job.cancel() // Мгновенная отмена всех вложенных корутин

// Тайм-ауты
withTimeout(5000) {
    networkRequest() // Выбросит TimeoutCancellationException если > 5 сек
}

Интеграция с жизненным циклом Android

class MyActivity : AppCompatActivity() {
    private val scope = lifecycleScope
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        scope.launch {
            // Автоматически отменится при уничтожении Activity
            loadData()
        }
    }
}

Когда использовать что?

Используйте потоки когда:

  • Работаете с блокирующими API (старые библиотеки, файловые операции без async)
  • Требуется истинный параллелизм на многоядерных системах
  • Работаете в средах без поддержки корутин

Используйте корутины когда:

  • Разрабатываете под Android или современные Kotlin-проекты
  • Нужна эффективная работа с сетью, базой данных
  • Хотите избежать callback hell
  • Требуется тесная интеграция с жизненным циклом компонентов

Заключение

Корутины — это эволюция подходов к асинхронному программированию. Они не заменяют потоки полностью, но предоставляют более высокоуровневую абстракцию для работы с асинхронными операциями. В Android-разработке корутины стали де-факто стандартом благодаря своей эффективности, читаемости кода и отличной интеграции с архитектурными компонентами Jetpack.