Ответ
Да, потоки могут взаимодействовать через общие ресурсы (память, файлы) или механизмы синхронизации. Основные способы:
- Общая память - потоки имеют доступ к одним и тем же объектам/переменным. Требуется синхронизация:
var counter = 0
synchronized(this) {
counter++
}
- Handler/Looper - отправка сообщений между потоками:
val handler = Handler(Looper.getMainLooper())
thread {
handler.post { updateUI() }
}
- Корутины - более современный способ с Channel/Flow:
val channel = Channel<Int>()
launch { channel.send(42) }
launch { println(channel.receive()) }
- BlockingQueue - потокобезопасные коллекции
Важно синхронизировать доступ к общим данным, чтобы избежать race conditions и deadlock. Для UI-операций обязательно использовать главный поток.
Ответ 18+ 🔞
А, ну это классика, ёпта! Смотри, тут всё просто, как два пальца обоссать. Потоки — они как соседи по коммуналке: живут в одном пространстве, но если не договориться — будет пиздец.
Вот смотри, основные способы, как они друг с другом общаются:
- Общая память — это типа как холодильник на всех. Все лезут, хватают, что хотят. Без правил — последний йогурт съедят, а тебе достанется пустая банка. Поэтому нужен
synchronized, это как записка «Руки прочь, это моя колбаса!».
var counter = 0
synchronized(this) { // Вот этот блок — твой замок на холодильнике
counter++ // Тут уже можно спокойно жрать, никто не отнимет
}
Без этой штуки будет race condition — представь, два потока одновременно пытаются колбасу откусить. В итоге счётчик будет кривой, а колбасы нихуя не останется.
- Handler/Looper — это уже цивилизованнее. Один поток (обычно главный, UI-шный) сидит, ждёт сообщений в почтовом ящике. Другой поток шлёт ему записку: «Эй, братан, обнови интерфейс, а то у пользователя глаза на лоб полезут!». Главный поток получает и выполняет. Красота.
val handler = Handler(Looper.getMainLooper()) // Наш почтальон для главной улицы
thread {
// Делаем что-то тяжёлое в сторонке...
handler.post { updateUI() } // И шлём записку: «Сделай уже это на главной!»
}
- Корутины с Channel/Flow — это вообще высший пилотаж, как будто провели оптоволокно между потоками. Один шлёт, другой принимает, всё асинхронно, без напряга.
val channel = Channel<Int>() // Наша труба связи
launch { channel.send(42) } // Кинул в трубу число
launch { println(channel.receive()) } // Достал из трубы число. Магия!
- BlockingQueue — это такие потоко-безопасные контейнеры. Бросаешь в него задание из одного потока, другой вынимает и делает. Типа конвейера на заводе. Удобно, проверено.
Главное правило, блядь, которое выбить себе на лбу: если потоки лезут в одни и те же данные — их надо синхронизировать, а то будет бардак, волнение ебать! Иначе race conditions (гонки данных) или deadlock (взаимная блокировка, когда все ждут друг друга, как идиоты).
И запомни раз и навсегда, ядрёна вошь: всё, что касается интерфейса (UI), обязательно делай на главном потоке. Иначе приложение просто рухнет, и пользователь охуеет от твоего кривого кода. Не хочешь же ты быть тем самым пидарасом шерстяным, чьё приложение постоянно вылетает? Вот то-то же.