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

Какие знаешь инструменты для работы с многопоточностью в Kotlin?

2.0 Middle🔥 211 комментариев
#Kotlin основы#Многопоточность и асинхронность

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

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

Какие знаешь инструменты для работы с многопоточностью в Kotlin?

В Kotlin есть несколько инструментов для работы с многопоточностью и асинхронностью. Расскажу про основные.

1. Thread (самый простой, но редко нужен)

// Создать и запустить поток
Thread {
    println("Running in thread: ${Thread.currentThread().name}")
    Thread.sleep(1000)
    println("Done")
}.start()

// С именем
thread(name = "MyThread") {
    println("Custom thread")
}

Использование: крайне редко, только для простых случаев.

Проблема: каждый поток занимает 1MB памяти, нельзя создать 10000 потоков.

2. Coroutines (основной инструмент)

// launch: не возвращает результат
launch {
    val data = api.fetch()
    updateUI(data)
}

// async: возвращает результат
val result = async {
    api.fetch()
}.await()

// runBlocking: блокирует текущий поток
runBlocking {
    api.fetch()
}

// withContext: переключить контекст
val result = withContext(Dispatchers.IO) {
    api.fetch()  // На IO потоке
}

Преимущества:

  • Легковесные (миллионы корутин на одном приложении)
  • Простой синтаксис
  • Structured concurrency (управление жизненным циклом)

3. Dispatcher'ы (выбор потока)

// Main поток (UI обновления)
launch(Dispatchers.Main) {
    updateUI()
}

// IO поток (сетевые запросы, файлы, БД)
launch(Dispatchers.IO) {
    val data = database.getUser()
    val response = api.fetch()
}

// Default (CPU-intensive задачи)
launch(Dispatchers.Default) {
    val result = expensiveCalculation()
}

// Unlimited (не рекомендуется в 99% случаев)
launch(Dispatchers.Unconfined) {
    // Опасно!
}

4. Flow (реактивный поток данных)

// Создать Flow
fun getNumbers(): Flow<Int> = flow {
    for (i in 1..5) {
        delay(100)
        emit(i)  // Отправить значение
    }
}

// Использовать
getNumbers()
    .filter { it > 2 }
    .map { it * 2 }
    .collect { println(it) }

// С StateFlow (LiveData на стероидах)
val state = MutableStateFlow(0)
launch {
    state.collect { value ->
        println("State: $value")
    }
}
state.value = 42

5. Mutex и Synchronization

// Mutex: мьютекс для синхронизации
val mutex = Mutex()
var count = 0

launch {
    mutex.withLock {
        count++  // Безопасно
    }
}

// Semaphore: ограничить количество одновременных операций
val semaphore = Semaphore(3)
repeat(10) {
    launch {
        semaphore.acquire()
        try {
            println("Task $it")
            delay(100)
        } finally {
            semaphore.release()
        }
    }
}

6. Channel (общая очередь между корутинами)

// Создать канал
val channel = Channel<Int>()

// Отправитель
launch {
    channel.send(1)
    channel.send(2)
    channel.close()
}

// Получатель
launch {
    for (value in channel) {
        println(value)
    }
}

// Пример: producer-consumer
fun produceNumbers() = produce<Int> {
    var x = 1
    while (true) {
        send(x++)
        delay(100)
    }
}

launch {
    val numbers = produceNumbers()
    numbers.consumeEach { println(it) }
}

7. Atomic Variables (потокобезопасные переменные)

import java.util.concurrent.atomic.AtomicInteger

val counter = AtomicInteger(0)

launch {
    repeat(100) {
        counter.incrementAndGet()  // Потокобезопасно
    }
}

println(counter.get())

8. ConcurrentCollections (потокобезопасные коллекции)

import java.util.concurrent.ConcurrentHashMap

val map = ConcurrentHashMap<String, String>()

launch {
    map["key"] = "value"  // Потокобезопасно
}

launch {
    println(map["key"])
}

9. RxJava (устаревает, но еще используется)

// Observable
Observable.just(1, 2, 3)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe { value ->
        println(value)
    }

// Subject (like Channel)
val subject = PublishSubject.create<Int>()

subject.onNext(1)
subject.onNext(2)

subject.subscribe { println(it) }

10. Comparison таблица

ИнструментСложностьПотребление памятиИспользование
ThreadСредняяМного (1MB на поток)Редко
CoroutinesНизкаяМалоОСНОВНОЕ
FlowСредняяНормальноРеактивное программирование
MutexСредняяМинимумСинхронизация
ChannelСредняяНормальноОбщение между корутинами
AtomicНизкаяМинимумПотокобезопасные переменные
RxJavaВысокаяМногоПереходим на Flow

11. Structured Concurrency (золотой стандарт)

// Иерархия: родитель контролирует детей
val parentJob = Job()
val scope = CoroutineScope(Dispatchers.Main + parentJob)

scope.launch {
    launch { /* child 1 */ }
    launch { /* child 2 */ }
    // Если отменить parentJob, отменятся ВСЕ дети
}

parentJob.cancel()  // Отменить ВСЁ

12. Какой инструмент выбрать?

Нужна UI работа (обновление View)?
  → launch(Dispatchers.Main)

Нужен результат?
  → async { }.await()

Потребляю поток данных (от сервера, БД)?
  → Flow + collect

Нужна синхронизация?
  → Mutex.withLock

Одна корутина отправляет, другая получает?
  → Channel

Нужна потокобезопасная переменная?
  → AtomicInteger, ConcurrentHashMap

Сложный реактивный код?
  → Flow (или RxJava если старый проект)

13. Best Practices

✅ Используй Coroutines по умолчанию ✅ Используй viewModelScope в ViewModel ✅ Используй lifecycleScope в Activity/Fragment ✅ Используй Flow для потока данных ✅ Используй Dispatchers правильно (IO для сети/БД) ✅ Используй Structured Concurrency (Job родитель/ребенок)

❌ НЕ используй Thread вручную ❌ НЕ забывай про scope (не утекай корутины) ❌ НЕ вызывай delay() на Main потоке (блокируется) ❌ НЕ используй GlobalScope

Итог

  • Coroutines = основной инструмент для асинхронности
  • Flow = для реактивного программирования
  • Dispatcher'ы = выбор потока (Main, IO, Default)
  • Mutex = синхронизация между корутинами
  • Channel = общение между корутинами
  • Structured Concurrency = управление жизненным циклом
  • Забудь про Thread (корутины вместо них)