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

Что такое поток?

1.0 Junior🔥 231 комментариев
#Многопоточность и асинхронность

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

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

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

Поток (Thread) — параллельное выполнение кода

Поток — это независимый путь выполнения кода внутри процесса. Один процесс может содержать несколько потоков, которые выполняются параллельно и совместно используют память.

Основные концепции

Процесс vs Поток

  • Процесс — это отдельное приложение с собственной памятью и ресурсами
  • Поток — это часть процесса, которая делится памятью с другими потоками
Процесс приложения (MyApp)
├─ Main Thread (UI thread)
├─ Background Thread 1
├─ Background Thread 2
└─ Background Thread 3

Главный поток (Main/UI Thread)

В Android есть специальный главный поток, который обрабатывает:

  • Все события UI (click, touch, keyboard)
  • Отрисовка экрана (rendering)
  • Обновление всех View
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Весь этот код выполняется на Main Thread
    }
}

Создание потоков

1. Использование класса Thread

// ❌ Старый способ (не рекомендуется для Android)
val thread = Thread {
    // Код выполняется в отдельном потоке
    val result = loadDataFromNetwork()
    // ❌ ОШИБКА! Не можем обновлять UI отсюда
    // textView.text = result
}
thread.start()  // запуск

2. Использование Coroutines (современный подход)

// ✅ Хорошо — рекомендуемый способ
viewModelScope.launch {
    val result = withContext(Dispatchers.IO) {
        loadDataFromNetwork()  // выполняется на IO потоке
    }
    // Автоматически вернёмся на Main Thread
    textView.text = result  // безопасное обновление UI
}

3. Executor (тоже старовато, но иногда используется)

val executor = Executors.newFixedThreadPool(2)
executor.execute {
    val result = heavyComputation()
}

Основные проблемы при работе с потоками

1. Race Condition (состояние гонки)

Когда два потока одновременно меняют общую переменную:

var counter = 0

// Поток 1
Thread {
    for (i in 1..1000) counter++
}.start()

// Поток 2
Thread {
    for (i in 1..1000) counter++
}.start()

// counter может быть не 2000, а например 1500!
// Потому что операции перекрываются

Решение — синхронизация:

var counter = 0
val lock = Any()

Thread {
    synchronized(lock) {
        for (i in 1..1000) counter++
    }
}.start()

Thread {
    synchronized(lock) {
        for (i in 1..1000) counter++
    }
}.start()

// Теперь counter = 2000

2. Deadlock (мёртвая блокировка)

Когда потоки ждут друг друга и зависают:

// ❌ Deadlock — потоки ждут друг друга
val lock1 = Any()
val lock2 = Any()

Thread {
    synchronized(lock1) {
        Thread.sleep(100)
        synchronized(lock2) { }  // ждёт lock2
    }
}.start()

Thread {
    synchronized(lock2) {
        synchronized(lock1) { }  // ждёт lock1
    }
}.start()

// Оба потока зависли — deadlock!

3. Memory Visibility (видимость памяти)

Один поток может не видеть изменения, сделанные другим потоком:

var flag = false

// Поток 1
Thread {
    flag = true
}.start()

// Поток 2
while (!flag) {
    // Может никогда не выйти из цикла,
    // хотя flag был установлен в true!
}

// Решение — используй volatile
volatile var flag = false

Диспетчеры в Coroutines (современный подход)

// Dispatchers.Main — главный поток (UI)
launch(Dispatchers.Main) {
    textView.text = "Hello"  // безопасно обновляем UI
}

// Dispatchers.IO — для сети, БД, файлов
launch(Dispatchers.IO) {
    val data = database.getUser(id)  // не блокирует UI
    withContext(Dispatchers.Main) {
        textView.text = data.name  // обновляем UI
    }
}

// Dispatchers.Default — для вычислений
launch(Dispatchers.Default) {
    val result = fibonacci(40)  // интенсивное вычисление
    withContext(Dispatchers.Main) {
        textView.text = result.toString()
    }
}

Жизненный цикл потока

NEW
 ↓
RUNNABLE (может быть выполняется)
 ↓
RUNNING (выполняется)
 ↓
WAITING/BLOCKED (ждёт ресурс)
 ↓
TERMINATED (завершён)
val thread = Thread { println("Running") }
println(thread.state)  // NEW

thread.start()
println(thread.state)  // RUNNABLE или RUNNING

thread.join()  // ждёт завершения
println(thread.state)  // TERMINATED

Thread Safety в коллекциях

// ❌ НЕ потокобезопасно
val list = mutableListOf<String>()

Thread { list.add("Item 1") }.start()
Thread { list.add("Item 2") }.start()
// Race condition!

// ✅ Потокобезопасно
val list = Collections.synchronizedList(mutableListOf<String>())

Thread { list.add("Item 1") }.start()
Thread { list.add("Item 2") }.start()
// Безопасно

Лучшие практики в Android

// ✅ Используй ViewModel + Repository pattern
class DataViewModel(private val repository: DataRepository) : ViewModel() {
    val data = repository.getData()  // возвращает Flow
        .stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
}

// ✅ Используй coroutines
viewModelScope.launch {
    val result = repository.fetchData()
    uiState.value = result
}

// ✅ Не создавай вручную Thread в Android
// ❌ val thread = Thread { ... }.start()

Вывод

Поток — это независимый путь выполнения кода.

  • Main Thread — для UI операций
  • Background Threads — для долгих операций
  • Coroutines — модерный способ управления потоками
  • Race conditions — главная проблема при синхронизации
  • Никогда не обновляй UI из Background потока!
Что такое поток? | PrepBro