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ândtryLock()a returnatfalse. AruncăIllegalMonitorStateException; puneunlock()doar pe ramuratrue. - 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.