Lock: bu nədir, bloklama növləri və sinxronizasiyada istifadəsi

Müəllif: IT Sectr Dərc olunub: 2026-03-19 Oxuma vaxtı: 8 dəq

Lock — çoxaxınlı tətbiqlərdə kodun kritik bölmələrinə eksklüziv girişi təmin edən sinxronizasiya mexanizmidir. Oracle, 2024-ə görə, Lock interfeysi ənənəvi synchronized blokları ilə müqayisədə daha çevik sinxronizasiya nəzarəti təqdim edir, o cümlədən timeout ilə ələ keçirmə cəhdləri və bir neçə gözləmə növbəsi dəstəyi.

Əsas məqamlar

  • Lock — Java-da bloklamaların açıq idarə edilməsi üçün interfeys.
  • ReentrantLock — eyni axın tərəfindən təkrar ələ keçirmə dəstəyi ilə əsas tətbiq.
  • ReadWriteLock məhsuldarlığı artırmaq üçün bloklamaları oxu və yazıya ayırır.
  • Deadlock — eyni anda bir neçə bloklamadan istifadə edərkən əsas risk.
  • Synchronized-dan fərqli olaraq, Lock timeoutları və kəsilə bilən gözləməni dəstəkləyir.

Lock nədir?

Lock — java.util.concurrent.locks paketindən olan, məlumatlara girişi sinxronizasiya etmək üçün açıq ələ keçirmə və buraxma əməliyyatlarını təmin edən interfeysdir. Synchronized-dan fərqli olaraq, Lock proqramçıya bloklama mexanizmi üzərində tam nəzarət verir.

Tərif və sinxronizasiyada rolu

Lock interfeysi Java 5-də daxili synchronized mexanizminə alternativ olaraq ortaya çıxdı. Əsas metodlar — lock, unlock, tryLock və lockInterruptibly. Bloklamalar çoxaxınlı mühitdə məlumatlara təhlükəsiz girişi təşkil etməyə, yarış vəziyyətlərinin və məlumatların zədələnməsinin qarşısını almağa imkan verir.

Lock-un synchronized-dan əsas üstünlüyü — çeviklik. Proqramçı timeout ilə bloklamanı ələ keçirməyə cəhd edə, bloklamadan məhrumiyyətlə onun məşğulluğunu yoxlaya və ya müxtəlif prioritetlərlə bir neçə gözləmə növbəsi təşkil edə bilər.

İnkişaf tarixi

Java 5-də Lock interfeysi yaranana qədər sinxronizasiyanın yeganə yolu synchronized idi, o da məhdudiyyətlərdən əziyyət çəkirdi: timeoutların olmaması, gözləməni kəsmək mümkünsüzlüyü və tək növbə. Doug Lea java.util.concurrent paketini layihələndirərək Lock-u əsas tikinti bloku kimi daxil etdi.

Bloklama necə işləyir?

Bloklama daxili vəziyyət bayrağı və gözləmə növbəsi vasitəsilə girişi idarə edir. Axın lock() çağırdıqda, mexanizm bloklamanın sərbəst olub-olmadığını yoxlayır və ya onu ələ keçirir, ya da axını buraxılana qədər növbəyə yerləşdirir.

Atomik ələ keçirmə və buraxma

Hər hansı bloklamanın əsasında atomik müqayisə və yerləşdirmə (CAS) əməliyyatı dayanır. Lock() çağırışı zamanı axın məşğulluq bayrağını atomik olaraq yerləşdirməyə cəhd edir. Bayraq artıq yerləşdirilibsə, axın bloklanır. Unlock() zamanı bayraq sıfırlanır və gözləyən axınlardan biri oyadılır.

kotlin
import java.util.concurrent.locks.ReentrantLock

val lock = ReentrantLock()

fun performTask() {
    lock.lock()
    try {
        // kritik bölmə
        println("${Thread.currentThread().name} axını işləyir")
    } finally {
        lock.unlock()
    }
}

Gözləmə növbəsi və oyadılma

ReentrantLock daxildə ikitərəfli növbədən (CLH lock queue) istifadə edir, burada hər gözləyən axın düyünlə təmsil olunur. Bloklama buraxıldıqda, növbənin baş düyünü oyadılır. Fair (ədalətli) rejim FIFO sırasını təmin edir, unfair isə ötürmə qabiliyyətini artırmaq üçün yeni axının gözləyənlərdən əvvəl ələ keçirməsinə icazə verir.

Bloklamaların əsas növləri

Müasir Java-Stack-də bloklamaların bir neçə tətbiqi mövcuddur, hər biri konkret ssenarilər üçün optimallaşdırılmışdır. Düzgün bloklamanın seçilməsi birbaşa çoxaxınlı tətbiqin məhsuldarlığına və etibarlılığına təsir edir.

ReentrantLock

ReentrantLock — Lock-un əsas və ən çox istifadə edilən tətbiqidir. Eyni axın tərəfindən təkrar ələ keçirməyi dəstəkləyir: əgər axın artıq bloklamaya sahibdirsə, təkrar lock() çağırışı onu bloklamır. Bu, rekursiv çağırışlarda deadlock-un qarşısını alır.

ReentrantReadWriteLock

ReadWriteLock bloklamaları iki rejimə ayırır: oxu və yazı. Bir neçə axın eyni anda oxu bloklamasını saxlaya bilər, lakin yazı eksklüziv giriş tələb edir. Bu, tez-tez oxuma və nadir yazma zamanı məhsuldarlığı əhəmiyyətli dərəcədə artırır.

StampedLock

StampedLock — Java 8-də ortaya çıxan ən yeni tətbiqdir. Üç rejimi dəstəkləyir: yazı, oxu və optimist oxu. Optimist oxu digər axınları bloklamır və oxuduqdan sonra məlumatların etibarlılığını yoxlayır ki, bu da ReadWriteLock ilə müqayisədə 10-20% məhsuldarlıq artımı verir.

BloklamaJava versiyasıRejimlərMəhsuldarlıq
ReentrantLockJava 5eksklüzivyüksək
ReadWriteLockJava 5oxu + yazıorta
StampedLockJava 8oxu + yazı + optimisticçox yüksək

ReentrantLock və onun xüsusiyyətləri

ReentrantLock — Lock-un ən populyar tətbiqi olub, synchronized-da mövcud olmayan bir sıra imkanlar təqdim edir. Onun xüsusiyyətlərini başa düşmək çoxaxınlılıqla effektiv iş üçün zəruridir.

Bloklamanın ədalətliliyi (fairness)

ReentrantLock konstruktoru fair parametrini qəbul edir. True olduqda bloklama FIFO sırasını təmin edir, false olduqda yeni axının gözləyənlərdən əvvəl ələ keçirməsi mümkündür. Ədalətli rejim aclığın qarşısını alır, lakin növbənin saxlanması üçün əlavə xərclər səbəbindən ötürmə qabiliyyətini 10-20% azaldır.

Timeoutlar və kəsilə bilən gözləmə

Synchronized-dan fərqli olaraq, ReentrantLock tryLock-u timeout ilə dəstəkləyir. Bloklamanı göstərilən müddətdə ələ keçirmək mümkün olmadıqda, axın sonsuz bloklanmaq əvəzinə icranı davam etdirir. LockInterruptibly metodu Thread.interrupt() vasitəsilə gözləyən axını kəsməyə imkan verir.

kotlin
val lock = ReentrantLock()

fun tryTask() {
    if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {
        try {
            println("Bloklama ələ keçirildi")
        } finally {
            lock.unlock()
        }
    } else {
        println("Bloklamanı ələ keçirmək mümkün olmadı")
    }
}

Şərtlər (Conditions)

ReentrantLock newCondition() metodu vasitəsilə bir neçə şərt dəyişənini dəstəkləyir. Hər Condition-un öz gözləmə növbəsi var ki, bu da mürəkkəb oyadılma ssenarilərini təşkil etməyə imkan verir. Await() və signal() metodları synchronized bloklarından wait() və notify()-u əvəz etdi, lakin bir neçə növbə dəstəyi ilə.

ReadWriteLock və StampedLock

ReadWriteLock və StampedLock oxu əməliyyatlarının yazıdan üstün olduğu hallarda girişin optimallaşdırılması problemini həll edir. Onlar oxumanın yazmadan daha tez-tez baş verdiyi ssenarilərdə ReentrantLock-dan qat-qat səmərəlidir.

ReadWriteLock praktikada

ReadWriteLock interfeysi iki metoddan ibarətdir: readLock() və writeLock(). Oxu bloklaması eyni anda bir neçə axın tərəfindən saxlanıla bilər, yazı bloklaması isə yalnız bir axın tərəfindən. Tipik nümunə — axın təhlükəsiz keş: çoxlu axın məlumatları oxuyur və yalnız bir axın vaxtaşırı onları yeniləyir.

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 və optimist oxu

StampedLock üçüncü rejimi əlavə edir — tryOptimisticRead. Bu rejim digər axınları bloklamır, yalnız vəziyyətin ştampını (stamp) yadda saxlayır. Oxuduqdan sonra proqramçı validate(stamp) çağıraraq məlumatların oxuma zamanı dəyişib-dəyişmədiyini yoxlayır. Məlumatlar dəyişibsə, əməliyyat təkrarlanmalıdır.

Mobil inkişafda bloklamalar

Mobil tətbiqlərdə bloklamalar axınlar arasında ümumi məlumatlara girişi koordinasiya etmək üçün istifadə olunur. Lakin onların tətbiqi məhdud cihaz resursları və interfeysin cavabdehliyini qorumaq zərurəti səbəbindən xüsusi diqqət tələb edir.

Android-də bloklamalar (Kotlin)

Android-də ReentrantLock Room, keşlər və fayllarla işləyərkən faydalıdır. Əsas axında bloklamanı ələ keçirməmək vacibdir. Asinxron kod üçün axını bloklamayan, korutini dayandıran kotlinx.coroutines-dən korutinlər və Mutex daha üstündür.

iOS-da bloklamalar (Swift)

iOS-da NSLock-dan standart Lock daha az istifadə olunur — proqramçılar DispatchQueue-ya barrier bayraqları ilə və ya os_unfair_lock əməliyyat bloklamalarına üstünlük verirlər. Swift 5.7+ vəziyyəti avtomatik qoruyan actors vasitəsilə müasir sinxronizasiya mexanizmləri təmin edir.

swift
import Foundation

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

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

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

Deadlock-dan qaçmaq üçün tövsiyələr

Deadlock-dan qaçmaq üçün layihədə bütün bloklamaların vahid ələ keçirmə sırasına riayət edin. Uzun müddətli bloklamanın mümkün olduğu yerlərdə lock() əvəzinə timeout ilə tryLock istifadə edin. Ənənəvi bloklamalar əvəzinə Lock-Free alqoritmlərini (AtomicReference, ConcurrentHashMap) tətbiq etməyi düşünün.

Lock ilə işin ən yaxşı təcrübələri

Lock-un tətbiqi deadlock və məhsuldarlığın düşməsinin qarşısını alan bir neçə qaydaya riayət etməyi tələb edir. Bu təcrübələr Java icması tərəfindən java.util.concurrent paketinin 20 illik istifadəsi ərzində işlənib hazırlanmışdır.

Finally-də buraxma

Ən vacib nümunə — finally-də lock. Kritik bölmənin uğurla və ya istisna ilə bitməsindən asılı olmayaraq, bloklama buraxılmalıdır. Bu, digər axınların bir səhv səbəbindən əbədi bloklanmayacağına zəmanət verir. Kotlin-də bu nümunə withLock genişlənməsi vasitəsilə zərif şəkildə həll olunur.

Saxlama müddətinin minimallaşdırılması

Kritik bölmə maksimum qısa olmalıdır. Bloklama daxilində heç vaxt giriş-çıxış, şəbəkə sorğuları və ya uzunmüddətli hesablamalar yerinə yetirməyin. Əgər serverdən məlumat oxumaq lazımdırsa, əvvəlcə onları əldə edin, sonra yalnız paylaşılan vəziyyəti yeniləmək üçün bloklamanı ələ keçirin. Bu, rəqabəti azaldır və sistemin ötürmə qabiliyyətini artırır.

Vahid ələ keçirmə sırası

Bir neçə Lock ilə işləyərkən deadlock-un qarşısını almaq üçün bütün layihədə qlobal ələ keçirmə sırasını müəyyən edin. Əgər əvvəlcə lockA, sonra lockB ələ keçirilirsə — hər hansı tərs ardıcıllıq code review qaydaları ilə qadağan edilməlidir. Avtomatik yoxlama üçün SpotBugs və IntelliJ Inspections kimi statik analizatorlardan istifadə edin.

Tez-tez verilən suallar

Lock-un synchronized-dan fərqi nədir?

Lock — timeout və kəsilə bilən gözləmə imkanı olan açıq interfeysdir. synchronized avtomatik olaraq monitoru ələ keçirir və buraxır, lakin tryLock, lockInterruptibly və bir neçə Condition-dan istifadə etməyə imkan vermir. Lock daha çevikdir, lakin finally-də əl ilə buraxılma tələb edir.

Ədalətli bloklama (fair lock) nədir?

Ədalətli bloklama FIFO sırasını təmin edir: ən uzun gözləyən axın bloklamanı birinci alır. Ədalətsiz bloklama növbədən yan keçərək yeni axına giriş verə bilər ki, bu da ötürmə qabiliyyətini artırır, lakin gözləyən axınların aclığına səbəb ola bilər.

Lock istifadə edərkən deadlock-dan necə qaçınmaq olar?

Bütün bloklamaların sabit sırasına riayət edin, şərtsiz lock əvəzinə timeout ilə tryLock istifadə edin və eyni anda saxlanılan bloklamaların sayını minimuma endirin. Lock-Free məlumat strukturlarının tətbiqi də deadlock riskini azaldır.

Lock-da Condition nədir?

Condition — Lock üçün wait/notify analoqudur, bir neçə müstəqil gözləmə növbəsi təşkil etməyə imkan verir. Hər newCondition() çağırışı ayrıca növbə yaradır ki, bu da synchronized-in tək növbəsi ilə müqayisədə axınların oyadılması üzərində daha dəqiq nəzarət verir.

Mobil tətbiq üçün hansı Lock seçilməlidir?

Korutinləri olan Android üçün kotlinx.coroutines-dən Mutex istifadə edin — o, axını bloklamaq əvəzinə korutini dayandırır. Swift 5.7+ ilə iOS üçün vəziyyətə girişi avtomatik sinxronizasiya edən actors daha üstündür. ReentrantLock-u köhnə kod və aşağı səviyyəli ssenarilər üçün saxlayın.

Nəticə

  • Lock — java.util.concurrent.locks-dan açıq bloklama idarəetmə interfeysi.
  • ReentrantLock — təkrar ələ keçirmə və ədalətlilik dəstəyi ilə əsas tətbiq.
  • ReadWriteLock read-heavy ssenariləri üçün bloklamaları oxu və yazıya ayırır.
  • StampedLock maksimum məhsuldarlıq üçün optimist oxu əlavə edir.
  • Timeoutlar və Condition — Lock-un synchronized-dan əsas üstünlükləri.
  • Deadlock sabit ələ keçirmə sırası və tryLock istifadəsi ilə qarşısı alınır.
  • Mobil inkişafda korutinlər (Android) və actors (iOS) tövsiyə olunur.

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun