Комментарии (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 потока!