Lock: bu nima, blokirovka turlari va sinxronizatsiyada foydalanish

Muallif: IT Sectr Nashr etilgan: 2026-03-19 O'qish vaqti: 8 daq

Lock — bu ko'p oqimli ilovalarda kodning muhim bo'limlariga eksklyuziv kirishni ta'minlovchi sinxronizatsiya mexanizmidir. Oracle, 2024 ma'lumotlariga ko'ra, Lock interfeysi an'anaviy synchronized bloklari bilan solishtirganda sinxronizatsiyani yanada moslashuvchan boshqarishni ta'minlaydi, jumladan taym-aut bilan egallash urinishlari va bir nechta kutish navbatlarini qo'llab-quvvatlash.

Asosiy fikrlar

  • Lock — Java-da blokirovkalarni aniq boshqarish uchun interfeys.
  • ReentrantLock — bir xil oqim tomonidan qayta egallashni qo'llab-quvvatlovchi asosiy tatbiq.
  • ReadWriteLock unumdorlikni oshirish uchun blokirovkalarni o'qish va yozishga ajratadi.
  • Deadlock — bir vaqtning o'zida bir nechta blokirovkalardan foydalanishdagi asosiy xavf.
  • Synchronized-dan farqli o'laroq, Lock taym-autlarni va uziladigan kutishni qo'llab-quvvatlaydi.

Lock nima?

Lock — java.util.concurrent.locks paketidan ma'lumotlarga kirishni sinxronizatsiya qilish uchun aniq egallash va bo'shatish operatsiyalarini ta'minlovchi interfeys. Synchronized-dan farqli o'laroq, Lock dasturchiga blokirovka mexanizmi ustidan to'liq nazorat beradi.

Ta'rif va sinxronizatsiyadagi roli

Lock interfeysi Java 5 da o'rnatilgan synchronized mexanizmiga muqobil sifatida paydo bo'ldi. Asosiy metodlar — lock, unlock, tryLock va lockInterruptibly. Blokirovkalar ko'p oqimli muhitda ma'lumotlarga xavfsiz kirishni tashkil etishga, poyga holatlari va ma'lumotlarning buzilishining oldini olishga imkon beradi.

Lock-ning synchronized-dan asosiy ustunligi — moslashuvchanlik. Dasturchi taym-aut bilan blokirovkani egallashga urinishi, uni bandligini bloklanmasdan tekshirishi yoki turli ustuvorliklar bilan bir nechta kutish navbatlarini tashkil qilishi mumkin.

Rivojlanish tarixi

Java 5 da Lock interfeysi paydo bo'lgunga qadar sinxronizatsiyaning yagona usuli synchronized edi, u cheklovlardan aziyat chekardi: taym-autlarning yo'qligi, kutishni uzish imkoniyatining yo'qligi va bitta navbat. Dug Li java.util.concurrent paketini loyihalashtirib, Lock-ni asosiy qurilish bloki sifatida kiritdi.

Blokirovka qanday ishlaydi?

Blokirovka kirishni ichki holat bayrog'i va kutish navbati orqali boshqaradi. Oqim lock() chaqirganda, mexanizm blokirovkaning bo'sh yoki bandligini tekshiradi va uni egallaydi yoki bo'shatilgunga qadar oqimni navbatga joylashtiradi.

Atomik egallash va bo'shatish

Har qanday blokirovkaning asosida taqqoslash va o'rnatishning atomik operatsiyasi (CAS) yotadi. Lock() chaqirilganda, oqim bandlik bayrog'ini atomik ravishda o'rnatishga harakat qiladi. Agar bayroq allaqachon o'rnatilgan bo'lsa, oqim bloklanadi. Unlock() da bayroq qayta o'rnatiladi va kutayotgan oqimlardan biri uyg'otiladi.

kotlin
import java.util.concurrent.locks.ReentrantLock

val lock = ReentrantLock()

fun performTask() {
    lock.lock()
    try {
        // muhim bo'lim
        println("${Thread.currentThread().name} oqimi ishlayapti")
    } finally {
        lock.unlock()
    }
}

Kutish navbati va uyg'otish

ReentrantLock ichki ikki tomonlama navbatdan (CLH lock queue) foydalanadi, bunda har bir kutayotgan oqim tugun bilan ifodalanadi. Blokirovka bo'shatilganda, navbatning bosh tuguni uyg'otiladi. Fair (adolatli) rejim FIFO tartibini kafolatlaydi, unfair esa o'tkazuvchanlikni oshirish uchun yangi oqimning kutayotganlardan oldin egallashiga ruxsat beradi.

Blokirovkalarning asosiy turlari

Zamonaviy Java-stekda blokirovkalarning bir nechta tatbiqlari mavjud, har biri muayyan stsenariylar uchun optimallashtirilgan. To'g'ri blokirovkani tanlash ko'p oqimli ilovaning unumdorligi va ishonchliligiga bevosita ta'sir qiladi.

ReentrantLock

ReentrantLock — Lock-ning asosiy va eng ko'p ishlatiladigan tatbiqi. Bir xil oqim tomonidan qayta egallashni qo'llab-quvvatlaydi: agar oqim blokirovkaga ega bo'lsa, qayta lock() chaqiruvi uni bloklamaydi. Bu rekursiv chaqiruvlarda deadlock-ning oldini oladi.

ReentrantReadWriteLock

ReadWriteLock blokirovkalarni ikki rejimga ajratadi: o'qish va yozish. Bir nechta oqim bir vaqtning o'zida o'qish blokirovkasini ushlab turishi mumkin, ammo yozish eksklyuziv kirishni talab qiladi. Bu tez-tez o'qish va kamdan-kam yozishda unumdorlikni sezilarli darajada oshiradi.

StampedLock

StampedLock — Java 8 da paydo bo'lgan eng yangi tatbiq. Uch rejimni qo'llab-quvvatlaydi: yozish, o'qish va optimistik o'qish. Optimistik o'qish boshqa oqimlarni bloklamaydi va o'qishdan keyin ma'lumotlarning haqiqiyligini tekshiradi, bu ReadWriteLock bilan solishtirganda 10-20% unumdorlik oshishini beradi.

BlokirovkaJava versiyasiRejimlarUnumdorlik
ReentrantLockJava 5eksklyuzivyuqori
ReadWriteLockJava 5o'qish + yozisho'rtacha
StampedLockJava 8o'qish + yozish + optimisticjuda yuqori

ReentrantLock va uning xususiyatlari

ReentrantLock — Lock-ning eng mashhur tatbiqi bo'lib, synchronized-da mavjud bo'lmagan bir qator imkoniyatlarni taqdim etadi. Uning xususiyatlarini tushunish ko'p oqimlilik bilan samarali ishlash uchun zarurdir.

Blokirovkaning adolatliligi (fairness)

ReentrantLock konstruktori fair parametrini qabul qiladi. True bo'lganda blokirovka FIFO tartibini kafolatlaydi, false bo'lganda yangi oqimning kutayotganlardan oldin egallashi mumkin. Adolatli rejim ochlikning oldini oladi, ammo navbatni saqlash uchun qo'shimcha xarajatlar tufayli o'tkazuvchanlikni 10-20% kamaytiradi.

Taym-autlar va uziladigan kutish

Synchronized-dan farqli o'laroq, ReentrantLock tryLock-ni taym-aut bilan qo'llab-quvvatlaydi. Blokirovkani belgilangan vaqtda egallash mumkin bo'lmasa, oqim cheksiz bloklanish o'rniga bajarishni davom ettiradi. LockInterruptibly metodi Thread.interrupt() orqali kutayotgan oqimni uzishga imkon beradi.

kotlin
val lock = ReentrantLock()

fun tryTask() {
    if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {
        try {
            println("Blokirovka egallandi")
        } finally {
            lock.unlock()
        }
    } else {
        println("Blokirovkani egallash mumkin bo'lmadi")
    }
}

Shartlar (Conditions)

ReentrantLock newCondition() metodi orqali bir nechta shart o'zgaruvchilarini qo'llab-quvvatlaydi. Har bir Condition o'z kutish navbatiga ega, bu murakkab uyg'otish stsenariylarini tashkil qilishga imkon beradi. Await() va signal() metodlari synchronized bloklaridagi wait() va notify() o'rnini egalladi, ammo bir nechta navbatlarni qo'llab-quvvatlash bilan.

ReadWriteLock va StampedLock

ReadWriteLock va StampedLock o'qish operatsiyalari yozishdan ustun bo'lgan hollarda kirishni optimallashtirish masalasini hal qiladi. Ular o'qish yozishdan tez-tez sodir bo'ladigan stsenariylarda ReentrantLock-dan ancha samaralidir.

ReadWriteLock amalda

ReadWriteLock interfeysi ikkita metoddan iborat: readLock() va writeLock(). O'qish blokirovkasi bir vaqtning o'zida bir nechta oqim tomonidan ushlab turilishi mumkin, yozish blokirovkasi esa faqat bitta oqim tomonidan. Oddiy misol — oqim xavfsiz kesh: ko'plab oqimlar ma'lumotlarni o'qiydi va faqat bittasi vaqti-vaqti bilan ularni yangilaydi.

kotlin
class SafeCache<K, V> {
    private val map = mutableMapOf<K, V>()
    private val rwLock = ReentrantReadWriteLock()

    fun get(key: K): V? {
        rwLock.readLock().lock()
        return try { map[key] } finally { rwLock.readLock().unlock() }
    }

    fun put(key: K, value: V) {
        rwLock.writeLock().lock()
        return try { map[key] = value } finally { rwLock.writeLock().unlock() }
    }
}

StampedLock va optimistik o'qish

StampedLock uchinchi rejimni qo'shadi — tryOptimisticRead. Bu rejim boshqa oqimlarni bloklamaydi, faqat holat shtampini (stamp) eslab qoladi. O'qishdan so'ng dasturchi validate(stamp) chaqirib, ma'lumotlarning o'qish vaqtida o'zgarganligini tekshiradi. Agar ma'lumotlar o'zgargan bo'lsa, operatsiyani takrorlash kerak.

Mobil ishlab chiqishda blokirovkalar

Mobil ilovalarda blokirovkalar oqimlar o'rtasida umumiy ma'lumotlarga kirishni muvofiqlashtirish uchun ishlatiladi. Biroq, ularni qo'llash cheklangan qurilma resurslari va interfeysning javob beruvchanligini saqlash zarurati tufayli alohida ehtiyotkorlikni talab qiladi.

Android-da blokirovkalar (Kotlin)

Android-da ReentrantLock Room, keshlar va fayllar bilan ishlashda foydalidir. Asosiy oqimda hech qachon blokirovkani egallamaslikni yodda tuting. Asinxron kod uchun oqimni bloklamaydigan, balki korutinni to'xtatadigan kotlinx.coroutines dan korutinlar va Mutex afzalroqdir.

iOS-da blokirovkalar (Swift)

iOS-da NSLock dan standart Lock kamroq ishlatiladi — dasturchilar DispatchQueue ni barrier bayroqlari bilan yoki os_unfair_lock operatsion blokirovkalarini afzal ko'radilar. Swift 5.7+ holatni avtomatik himoya qiluvchi actors orqali zamonaviy sinxronizatsiya mexanizmlarini taqdim etadi.

swift
import Foundation

actor DataStore {
    private var items: [String] = []

    func add(_ item: String) {
        items.append(item)
    }

    func getAll() -> [String] {
        items
    }
}

Deadlock-dan qochish bo'yicha tavsiyalar

Deadlock-dan qochish uchun loyihadagi barcha blokirovkalarning yagona egallash tartibiga rioya qiling. Uzoq muddatli blokirovka mumkin bo'lgan joylarda lock() o'rniga taym-aut bilan tryLock dan foydalaning. An'anaviy blokirovkalar o'rniga Lock-Free algoritmlarini (AtomicReference, ConcurrentHashMap) qo'llashni o'ylab ko'ring.

Lock bilan ishlashning eng yaxshi amaliyotlari

Lock ni qo'llash deadlock va unumdorlikning pasayishining oldini oluvchi bir nechta qoidalarga rioya qilishni talab qiladi. Ushbu amaliyotlar Java jamiyati tomonidan java.util.concurrent paketining 20 yillik ishlatilishi davomida ishlab chiqilgan.

Finally-da bo'shatish

Eng muhim naqsh — finally-da lock. Muhim bo'lim muvaffaqiyatli yoki istisno bilan tugashidan qat'iy nazar, blokirovka bo'shatilishi kerak. Bu boshqa oqimlarning bitta xato tufayli abadiy bloklanmasligini kafolatlaydi. Kotlin-da bu naqsh withLock kengaytmasi orqali nafis hal qilinadi.

Saqlash vaqtini minimallashtirish

Muhim bo'lim maksimal darajada qisqa bo'lishi kerak. Blokirovka ichida hech qachon kiritish-chiqarish, tarmoq so'rovlari yoki uzoq muddatli hisob-kitoblarni bajarmang. Agar serverdan ma'lumotlarni o'qish kerak bo'lsa, avval ularni oling, keyin faqat umumiy holatni yangilash uchun blokirovkani egallang. Bu raqobatni kamaytiradi va tizimning o'tkazuvchanligini oshiradi.

Yagona egallash tartibi

Bir nechta Lock bilan ishlashda deadlock-ning oldini olish uchun butun loyihada global egallash tartibini belgilang. Agar avval lockA, keyin lockB egallansa — har qanday teskari ketma-ketlik code review qoidalari bilan taqiqlanishi kerak. Avtomatik tekshirish uchun SpotBugs va IntelliJ Inspections kabi statik analizatorlardan foydalaning.

Tez-tez so'raladigan savollar

Lock-ning synchronized-dan farqi nimada?

Lock — taym-aut va uziladigan kutish imkoniyatiga ega aniq interfeys. synchronized avtomatik ravishda monitorini egallaydi va bo'shatadi, lekin tryLock, lockInterruptibly va bir nechta Condition dan foydalanishga ruxsat bermaydi. Lock moslashuvchanroq, ammo finally-da qo'lda bo'shatishni talab qiladi.

Adolatli blokirovka (fair lock) nima?

Adolatli blokirovka FIFO tartibini kafolatlaydi: eng uzoq kutayotgan oqim blokirovkani birinchi oladi. Adolatsiz blokirovka navbatni chetlab o'tib, yangi oqimga kirish berishi mumkin, bu o'tkazuvchanlikni oshiradi, lekin kutayotgan oqimlarning ochligiga olib kelishi mumkin.

Lock ishlatganda deadlock-dan qanday qochish kerak?

Barcha blokirovkalarning barqaror tartibiga rioya qiling, shartsiz lock o'rniga taym-aut bilan tryLock dan foydalaning va bir vaqtda ushlab turiladigan blokirovkalar sonini minimallashtiring. Lock-Free ma'lumotlar tuzilmalarini qo'llash ham deadlock xavfini kamaytiradi.

Lock-da Condition nima?

Condition — Lock uchun wait/notify analogi bo'lib, bir nechta mustaqil kutish navbatlarini tashkil qilishga imkon beradi. Har bir newCondition() chaqiruvi alohida navbat yaratadi, bu synchronized-ning yagona navbati bilan solishtirganda oqimlarni uyg'otish ustidan aniqroq nazorat beradi.

Mobil ilova uchun qaysi Lock ni tanlash kerak?

Korutinli Android uchun kotlinx.coroutines dan Mutex dan foydalaning — u oqimni bloklash o'rniga korutinni to'xtatadi. Swift 5.7+ bilan iOS uchun holatga kirishni avtomatik sinxronizatsiya qiluvchi actors afzalroqdir. ReentrantLock-ni eski kod va past darajadagi stsenariylar uchun qoldiring.

Xulosa

  • Lock — java.util.concurrent.locks dan aniq blokirovka boshqarish interfeysi.
  • ReentrantLock — qayta egallash va adolatlilikni qo'llab-quvvatlovchi asosiy tatbiq.
  • ReadWriteLock read-heavy stsenariylari uchun blokirovkalarni o'qish va yozishga ajratadi.
  • StampedLock maksimal unumdorlik uchun optimistik o'qishni qo'shadi.
  • Taym-autlar va Condition — Lock-ning synchronized-dan asosiy ustunliklari.
  • Deadlock barqaror egallash tartibi va tryLock yordamida oldini olinadi.
  • Mobil ishlab chiqishda korutinlar (Android) va actors (iOS) tavsiya etiladi.

Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz

IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.

Loyihani muhokama qilish

Shuningdek o'qing