ReentrantLock.tryLock în Java

Încearcă să ia lacătul fără să aștepte la nesfârșit: returnează true dacă l-a luat, false dacă este ocupat.

Sintaxă
boolean tryLock()
boolean tryLock(long timp, TimeUnit unitate) throws InterruptedException

Ce face ReentrantLock.tryLock

tryLock() ia lacătul doar dacă este liber în acel moment și returnează true; dacă îl ține alt fir, returnează imediat false, fără să aștepte. tryLock(timp, unitate) așteaptă cel mult timpul dat.

Este util când preferi să faci altceva decât să stai blocat: afișezi „sistem ocupat, încearcă mai târziu” sau sari peste o operație care nu este obligatorie. Ajută și la evitarea blocajelor reciproce (deadlock), când două fire așteaptă fiecare lacătul celuilalt.

Când tryLock returnează true, ești obligat să apelezi unlock() în finally. Când returnează false, nu ai lacătul și nu ai voie să apelezi unlock().

Parametri

Parametru Descriere
timp Cât să aștepte cel mult (doar la varianta cu timp).
unitate Unitatea pentru timp.

Ce returnează

true dacă lacătul a fost luat, altfel false.

Exemple

Exemplul 1: Lacătul este ocupat de alt fir

Un fir ține lacătul o secundă. tryLock() din main eșuează imediat; după eliberare, tryLock cu timp reușește.

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock imprimanta = new ReentrantLock();
        CountDownLatch aLuatLacatul = new CountDownLatch(1);
        Thread ana = new Thread(() -> {
            imprimanta.lock();
            try {
                aLuatLacatul.countDown();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                imprimanta.unlock();
            }
        });
        ana.start();
        aLuatLacatul.await();
        System.out.println("tryLock() cât tipărește Ana: " + imprimanta.tryLock());
        if (imprimanta.tryLock(3, TimeUnit.SECONDS)) {
            try {
                System.out.println("Am primit imprimanta după ce a terminat Ana");
            } finally {
                imprimanta.unlock();
            }
        }
        ana.join();
    }
}

Rezultat

tryLock() cât tipărește Ana: false
Am primit imprimanta după ce a terminat Ana

Exemplul 2: Lacăt liber, lacăt reluat de același fir

Aici nu există alt fir care să țină lacătul, deci tryLock() reușește de fiecare dată. Lacătul este reentrant: firul care îl ține deja îl poate lua din nou, deci și apelul „din nou” reușește. Ramura else ar rula doar dacă alt fir ar ține lacătul, ca în primul exemplu.

import java.util.concurrent.locks.ReentrantLock;

public class Main {
    static final ReentrantLock BACKUP = new ReentrantLock();

    static void incearcaBackup(String cine) {
        if (BACKUP.tryLock()) {
            try {
                System.out.println(cine + ": pornesc copia de siguranță");
                if (cine.equals("Ana")) incearcaBackup("Ana (din nou)");
            } finally {
                BACKUP.unlock();
            }
        } else {
            System.out.println(cine + ": backup ocupat, sar peste");
        }
    }

    public static void main(String[] args) {
        incearcaBackup("Ana");
        incearcaBackup("Mihai");
    }
}

Rezultat

Ana: pornesc copia de siguranță
Ana (din nou): pornesc copia de siguranță
Mihai: pornesc copia de siguranță

Greșeli frecvente

  • Apelezi unlock() și când tryLock() a returnat false. Aruncă IllegalMonitorStateException; pune unlock() doar pe ramura true.
  • Ignori valoarea returnată de tryLock() și intri oricum în zona protejată.

Comenzi înrudite

  • ReentrantLock: Un lacăt: lock() lasă un singur fir să intre în zona protejată, unlock() îl eliberează; unlock se pune mereu în finally.
  • TimeUnit: Enumerarea unităților de timp (SECONDS, MINUTES…) folosită la așteptări; face conversii și pauze ușor de citit.
  • CountDownLatch: Un numărător pentru așteptare: await() blochează până când alte fire apelează countDown() de N ori.