Что такое потокобезопасный синглтон

Ответ

Потокобезопасный синглтон гарантирует, что только один экземпляр класса будет создан в многопоточной среде.

Способы реализации в Kotlin:

  1. object (ленивая инициализация, потокобезопасно по умолчанию):
object Singleton
  1. Double-Checked Locking (оптимизирован для Java/Kotlin):
class Singleton private constructor() {
    companion object {
        @Volatile private var instance: Singleton? = null
        fun getInstance(): Singleton = instance ?: synchronized(this) {
            instance ?: Singleton().also { instance = it }
        }
    }
}

Ключевые моменты:

  • @Volatile предотвращает кэширование переменной в потоках.
  • synchronized блокирует создание экземпляра в многопоточной среде.
  • Ленивая инициализация (by lazy) в Kotlin тоже потокобезопасна по умолчанию.

Ответ 18+ 🔞

А, ну это классика, ёпта! Ты знаешь, как иногда бывает — нужна тебе одна штука на весь проект, одна-единственная, а её на всех не хватает. Вот представь: у тебя есть последняя печенька в офисе, и если её не охранять, то какой-нибудь хитрожопый коллега её сожрёт, пока ты кофе наливаешь. Так и с синглтоном — нужно сделать так, чтобы эта «печенька» была одна, и чтобы даже если двадцать потоков одновременно на неё набросятся, они не поругались и не создали двадцать одинаковых печенек. Это пиздец как важно, когда ресурс общий.

Вот, смотри, в Котлине есть способ, от которого просто терпения ноль ебать — настолько он простой. Просто пишешь object Singleton, и всё, приехали. Компилятор сам под капотом делает всё, чтобы эта штука создалась один раз и была безопасной для потоков. Это как волшебство, ни хуя себе!

object Singleton

Вот и весь разговор. Но если тебе, чувак, интересно, как это раньше, в старые добрые времена, делали вручную (или если ты из тех, кто любит поковыряться в грязи), то есть паттерн «Double-Checked Locking». Выглядит он, конечно, пиздопроебибна, но логика в нём есть.

class Singleton private constructor() {
    companion object {
        @Volatile private var instance: Singleton? = null
        fun getInstance(): Singleton = instance ?: synchronized(this) {
            instance ?: Singleton().also { instance = it }
        }
    }
}

Давай разберём эту манду с ушами:

  1. @Volatile — это такая примочка для переменной. Она кричит всем потокам: «Эй, мудаки, не кэшируйте меня у себя в ядре, читайте самое свежее значение из оперативки!» Без этого один поток мог бы создать экземпляр, а другой его даже не увидеть — доверия ебать ноль к таким ситуациям.
  2. synchronized(this) — это наш вышибала, охранник у входа. Он говорит: «Ребята, создавать экземпляр будет только один из вас, остальные — постойте тут, побздите немного». Он блокирует кусок кода, чтобы туда не вломилась толпа.
  3. А дальше идёт проверка внутри блока: «А может, пока я стоял в очереди, какой-то пидарас шерстяной уже всё создал?» Если да — возвращаем его экземпляр. Если нет — создаём свой и запоминаем.

Ключевые моменты, чтобы не облажаться:

  • Этот @Volatile — не просто так, он реально нужен, иначе будет хиросима и нигерсраки в многопоточке.
  • В Котлине, если честно, проще всего использовать by lazy для свойств — эта штука тоже по умолчанию потокобезопасная. То есть можно не городить этот ёперный театр с блокировками, а довериться языку. Но знать, как оно работает под капотом — это всегда полезно, а то подозрение ебать чувствую, что однажды это вылезет на собеседовании.

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