-
Notifications
You must be signed in to change notification settings - Fork 0
Lock Types
Garvit Joshi edited this page Jan 8, 2026
·
3 revisions
Locksmith supports three types of locks for different concurrency patterns.
| Type | Concurrency | Use Case |
|---|---|---|
REENTRANT |
Exclusive | Default - one holder at a time |
READ |
Shared | Multiple concurrent readers |
WRITE |
Exclusive | Single writer, blocks all |
A reentrant lock allows only one holder at a time. The same thread can acquire it multiple times (reentrant):
@DistributedLock(key = "resource") // type defaults to REENTRANT
public void exclusiveAccess() {
// Only one instance executes this at a time
}
// Explicit declaration
@DistributedLock(key = "resource", type = LockType.REENTRANT)
public void exclusiveAccess() { }The same thread can acquire the lock multiple times:
@Service
public class NestedService {
@DistributedLock(key = "resource")
public void outer() {
// Lock acquired (count: 1)
inner(); // Same thread can re-enter
// Lock still held (count back to 1)
}
@DistributedLock(key = "resource")
public void inner() {
// Lock count: 2 (re-entered)
// ...
} // Lock count: 1 (not released yet)
}- Default choice for most operations
- When you need exclusive access to a resource
- When nested calls might use the same lock
A read lock allows multiple concurrent readers, but blocks if a write lock is held:
@DistributedLock(key = "document", type = LockType.READ)
public Document readDocument() {
// Multiple instances can read simultaneously
return fetchDocument();
}Instance A (READ) Instance B (READ) Instance C (READ)
│ │ │
▼ ▼ ▼
Acquire READ ────────────► Acquire READ ────────────► Acquire READ
│ │ │
│ (reading) │ (reading) │ (reading)
│ │ │
▼ ▼ ▼
Release READ Release READ Release READ
All three readers execute concurrently.
- Read-heavy workloads
- Caching scenarios
- Report generation from shared data
- Any operation that doesn't modify state
A write lock is exclusive - it blocks all readers and other writers:
@DistributedLock(key = "document", type = LockType.WRITE)
public void writeDocument(Document doc) {
// Exclusive access - no readers or writers allowed
saveDocument(doc);
}Instance A (WRITE) Instance B (READ) Instance C (WRITE)
│ │ │
▼ ▼ ▼
Acquire WRITE ──────────► Try READ Try WRITE
│ │ │
│ (writing) ▼ ▼
│ BLOCKED BLOCKED
│ │ │
▼ │ │
Release WRITE ─────────────────┼──────────────────────────┤
▼ │
ACQUIRED │
│ (reading) │
▼ │
Release READ ───────────────────┤
▼
ACQUIRED
- Modifying shared state
- Database updates
- File writes
- Any operation that changes data
Combine READ and WRITE locks on the same key for safe concurrent access:
@Service
public class DocumentService {
// Multiple readers allowed
@DistributedLock(key = "#{'doc-' + #docId}", type = LockType.READ)
public Document getDocument(String docId) {
return documentRepository.findById(docId);
}
// Exclusive write access
@DistributedLock(key = "#{'doc-' + #docId}", type = LockType.WRITE)
public void updateDocument(String docId, Document doc) {
documentRepository.save(doc);
}
// Exclusive delete access
@DistributedLock(key = "#{'doc-' + #docId}", type = LockType.WRITE)
public void deleteDocument(String docId) {
documentRepository.delete(docId);
}
}| Holding / Requesting | READ | WRITE |
|---|---|---|
| None | Granted | Granted |
| READ | Granted | Blocked |
| WRITE | Blocked | Blocked |
@Service
public class ConfigService {
@DistributedLock(key = "app-config", type = LockType.READ)
public Config getConfig() {
// Many instances can read config simultaneously
return configRepository.load();
}
@DistributedLock(key = "app-config", type = LockType.WRITE)
public void updateConfig(Config config) {
// Exclusive access during update
// All readers blocked until complete
configRepository.save(config);
notifyConfigChange();
}
}@Service
public class InventoryService {
@DistributedLock(key = "#{'inventory-' + #productId}", type = LockType.READ)
public int getStock(String productId) {
// Concurrent stock checks allowed
return inventoryRepository.getQuantity(productId);
}
@DistributedLock(key = "#{'inventory-' + #productId}", type = LockType.WRITE)
public void adjustStock(String productId, int delta) {
// Exclusive access for stock changes
int current = inventoryRepository.getQuantity(productId);
inventoryRepository.setQuantity(productId, current + delta);
}
}@Service
public class SessionService {
@DistributedLock(key = "#{'session-' + #userId}", type = LockType.READ)
public Session getSession(String userId) {
return sessionStore.get(userId);
}
@DistributedLock(key = "#{'session-' + #userId}", type = LockType.WRITE)
public void updateSession(String userId, Session session) {
sessionStore.put(userId, session);
}
@DistributedLock(key = "#{'session-' + #userId}", type = LockType.WRITE)
public void invalidateSession(String userId) {
sessionStore.remove(userId);
}
}@DistributedLock(
key = "large-report",
type = LockType.READ,
autoRenew = true
)
public Report generateLargeReport() {
// Long-running read with auto-renewal
return buildReport();
}@DistributedLock(
key = "#{'resource-' + #id}",
type = LockType.WRITE,
mode = AcquisitionMode.WAIT_AND_SKIP,
waitTime = "30s"
)
public void updateResource(String id, Data data) {
// Wait up to 30s for write lock
}@DistributedLock(
key = "shared-cache",
type = LockType.READ,
skipHandler = CachedResultHandler.class
)
public Data readData() {
// If lock unavailable, return cached result
}All methods accessing the same resource must use the same lock key:
// CORRECT - same key
@DistributedLock(key = "#{'user-' + #id}", type = LockType.READ)
public User getUser(String id) { }
@DistributedLock(key = "#{'user-' + #id}", type = LockType.WRITE)
public void updateUser(String id, User user) { }
// WRONG - different keys won't synchronize!
@DistributedLock(key = "#{'read-user-' + #id}", type = LockType.READ)
public User getUser(String id) { }
@DistributedLock(key = "#{'write-user-' + #id}", type = LockType.WRITE)
public void updateUser(String id, User user) { }With many readers, writers might wait indefinitely. Consider:
- Using
WAIT_AND_SKIPwith reasonable timeout for writers - Implementing fair locking at application level
- Batching writes during low-traffic periods
| Lock Type | Overhead | Throughput |
|---|---|---|
| REENTRANT | Low | Single operation at a time |
| READ | Medium | High for read-heavy workloads |
| WRITE | Medium | Limited by write contention |
- Auto-Renew Lease Time - Automatic lock extension
- Lease Expiration Detection - Catch long-running operations
