Skip to content

Lock Types

Garvit Joshi edited this page Jan 8, 2026 · 3 revisions

Lock Types

Locksmith supports three types of locks for different concurrency patterns.

Available Types

Type Concurrency Use Case
REENTRANT Exclusive Default - one holder at a time
READ Shared Multiple concurrent readers
WRITE Exclusive Single writer, blocks all

REENTRANT (Default)

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() { }

Reentrancy

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)
}

When to Use

  • Default choice for most operations
  • When you need exclusive access to a resource
  • When nested calls might use the same lock

READ 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();
}

Read Lock Behavior

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.

When to Use

  • Read-heavy workloads
  • Caching scenarios
  • Report generation from shared data
  • Any operation that doesn't modify state

WRITE Lock

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);
}

Write Lock Behavior

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

When to Use

  • Modifying shared state
  • Database updates
  • File writes
  • Any operation that changes data

Read/Write Pattern

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);
    }
}

Concurrency Matrix

Holding / Requesting READ WRITE
None Granted Granted
READ Granted Blocked
WRITE Blocked Blocked

Real-World Examples

Configuration Management

@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();
    }
}

Inventory Management

@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);
    }
}

User Session Management

@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);
    }
}

Combining with Other Features

With Auto-Renew

@DistributedLock(
    key = "large-report",
    type = LockType.READ,
    autoRenew = true
)
public Report generateLargeReport() {
    // Long-running read with auto-renewal
    return buildReport();
}

With Wait Mode

@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
}

With Skip Handlers

@DistributedLock(
    key = "shared-cache",
    type = LockType.READ,
    skipHandler = CachedResultHandler.class
)
public Data readData() {
    // If lock unavailable, return cached result
}

Important Considerations

Key Consistency

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) { }

Writer Starvation

With many readers, writers might wait indefinitely. Consider:

  • Using WAIT_AND_SKIP with reasonable timeout for writers
  • Implementing fair locking at application level
  • Batching writes during low-traffic periods

Performance

Lock Type Overhead Throughput
REENTRANT Low Single operation at a time
READ Medium High for read-heavy workloads
WRITE Medium Limited by write contention

Next Steps

Clone this wiki locally