# Code Locking

> BoxLang provides powerful thread-safe locking mechanisms for synchronizing access to shared resources with named locks, scope locks, and full Java interop support.

Locking is essential for multi-threaded applications where multiple requests or threads access shared resources. BoxLang's locking system uses Java's **ReentrantReadWriteLock** internally, providing robust, high-performance synchronization with support for both exclusive (write) and readonly (read) locks.

## 📋 Table of Contents

* [Lock Basics](#lock-basics)
* [Lock Types](#lock-types)
* [Named Locking](#named-locking)
* [Scope Locking](#scope-locking)
* [Lock Attributes](#lock-attributes)
* [Double-Check Locking Pattern](#double-check-locking-pattern)
* [Deadlock Prevention](#deadlock-prevention)
* [Advanced: Java Interop Locking](#advanced-java-interop-locking)
* [Best Practices](#best-practices)

## 🎯 Lock Basics

BoxLang provides the `lock` component/construct for thread-safe synchronization:

```js
// Script syntax
lock name="mylock" timeout=10 {
    // Synchronized code here
    criticalOperation()
}

// Template syntax
<bx:lock name="mylock" timeout="10">
    <!-- Synchronized code here -->
</bx:lock>

// CFML compatibility
<cflock name="mylock" timeout="10">
    <!-- Synchronized code here -->
</cflock>
```

### When to Use Locks

Use locks when:

* ✅ Multiple threads access shared data
* ✅ Writing to shared variables (application, server, session scopes)
* ✅ Reading shared data that might be modified
* ✅ Ensuring atomic operations
* ✅ Preventing race conditions
* ✅ Coordinating access to external resources

### Lock Flow Diagram

{% @mermaid/diagram content="graph TD
A\[Thread requests lock] --> B{Lock available?}
B -->|Yes| C\[Acquire lock]
B -->|No| D{Within timeout?}
D -->|Yes| E\[Wait in queue]
D -->|No| F{throwOnTimeout?}
E --> B
F -->|true| G\[Throw LockException]
F -->|false| H\[Skip body, continue]
C --> I\[Execute locked code]
I --> J\[Release lock]
J --> K\[Next thread gets lock]

```
style C fill:#ccffcc
style G fill:#ffcccc
style I fill:#cce5ff" %}
```

## 🔐 Lock Types

BoxLang supports two lock types based on Java's **ReentrantReadWriteLock**:

### 1. Exclusive Locks (Write Locks)

**Exclusive locks** allow only **ONE thread** to access the locked code at a time. Use for read/write operations on shared data.

```js
lock name="config-update" type="exclusive" timeout=10 {
    // Only ONE thread can execute this at a time
    application.config = loadNewConfig()
    application.lastUpdate = now()
}
```

**Characteristics:**

* ✅ Single-thread access only
* ✅ Blocks all other threads (both exclusive and readonly)
* ✅ Use for writing or modifying shared data
* ✅ First-come, first-served basis

### 2. Readonly Locks (Read Locks)

**Readonly locks** allow **MULTIPLE threads** to access the locked code simultaneously. Use for read-only operations on shared data.

```js
lock name="config-read" type="readonly" timeout=10 {
    // Multiple threads can execute this concurrently
    myValue = application.config.getSetting( "key" )
}
```

**Characteristics:**

* ✅ Multiple concurrent readers allowed
* ✅ Blocked by exclusive locks
* ✅ Use for reading shared data
* ✅ Better performance for read-heavy workloads

### Lock Type Comparison

| Feature               | Exclusive            | Readonly                 |
| --------------------- | -------------------- | ------------------------ |
| **Concurrent Access** | ❌ No (single thread) | ✅ Yes (multiple threads) |
| **Blocks Exclusive**  | ✅ Yes                | ✅ Yes                    |
| **Blocks Readonly**   | ✅ Yes                | ❌ No                     |
| **Use For**           | Writing/Modifying    | Reading only             |
| **Performance**       | Lower throughput     | Higher throughput        |
| **Default**           | ✅ Yes                | ❌ No                     |

### Lock Type Interactions

{% @mermaid/diagram content="graph LR
A\[Exclusive Lock] -->|Blocks| B\[Exclusive Lock]
A -->|Blocks| C\[Readonly Lock]
D\[Readonly Lock] -->|Blocks| E\[Exclusive Lock]
D -->|Allows| F\[Readonly Lock]

```
style A fill:#ffcccc
style D fill:#ccffcc
style F fill:#ccffcc" %}
```

## 📛 Named Locking

**Named locks** use a string identifier to coordinate access across the entire BoxLang server.

### Basic Named Lock

```js
lock name="cache-population" timeout=10 type="exclusive" {
    if ( !cache.exists( "mykey" ) ) {
        myData = expensiveOperation()
        cache.set( "mykey", myData )
    }
}
```

{% hint style="danger" %}
**Global Scope**: Lock names are **server-wide** and shared across ALL applications running on the same BoxLang instance. Use unique, descriptive names to avoid conflicts between applications.
{% endhint %}

### Naming Conventions

Use descriptive, namespaced lock names to avoid collisions:

```js
// ❌ Bad: Generic names risk conflicts
lock name="update" timeout=10 { }
lock name="cache" timeout=10 { }

// ✅ Good: App-prefixed names
lock name="myapp-config-update" timeout=10 { }
lock name="myapp-cache-users" timeout=10 { }

// ✅ Good: Module-prefixed names
lock name="shop.cart.checkout" timeout=10 { }
lock name="cms.content.publish" timeout=10 { }

// ✅ Good: Include resource identifier
lock name="user-profile-#userId#" timeout=10 { }
lock name="order-process-#orderId#" timeout=10 { }
```

### Lock Name Characteristics

* **Case-Sensitive**: `"MyLock"` and `"mylock"` are different locks
* **Server-Wide**: Shared across all applications on the server
* **Dynamic**: Can use variables/expressions in names
* **Must be Non-Empty**: Empty strings not allowed

### Named Lock Examples

#### Example 1: Cache Population

```js
function getUserData( userId ) {
    cacheKey = "user-#userId#"
    data = cache.get( cacheKey )

    if ( isNull( data ) ) {
        // Use lock to prevent thundering herd
        lock name="user-cache-#userId#" timeout=5 type="exclusive" {
            // Double-check inside lock
            data = cache.get( cacheKey )
            if ( isNull( data ) ) {
                data = db.getUserById( userId )
                cache.set( cacheKey, data )
            }
        }
    }

    return data
}
```

#### Example 2: Counter Increment

```js
lock name="visitor-counter" timeout=10 type="exclusive" {
    application.visitorCount = ( application.visitorCount ?: 0 ) + 1
}
```

#### Example 3: Multiple Locks with Different Names

```js
// Lock for user profile updates
lock name="profile-update-#userId#" timeout=10 type="exclusive" {
    updateUserProfile( userId, profileData )
}

// Lock for account balance updates
lock name="balance-update-#userId#" timeout=10 type="exclusive" {
    updateAccountBalance( userId, amount )
}

// These locks are independent - they don't block each other
```

## 🎯 Scope Locking

**Scope locks** synchronize access to entire BoxLang scopes: `application`, `server`, `session`, `request`.

### Scope Lock Syntax

```js
lock scope="application" timeout=10 type="exclusive" {
    application.counter += 1
}

lock scope="session" timeout=10 type="readonly" {
    userName = session.user.name
}
```

### How Scope Locks Work

Each scope has a unique internal lock name generated from the scope name and instance hash:

```java
// From BaseScope.java
lockName = scopeName.getName() + new Object().hashCode()
```

This ensures each scope instance (e.g., each session) has its own lock.

### Available Scopes

| Scope             | Lock Granularity | Use Case                     |
| ----------------- | ---------------- | ---------------------------- |
| **`application`** | Per application  | Application-wide shared data |
| **`server`**      | Per server       | Server-wide shared data      |
| **`session`**     | Per session      | User-specific session data   |
| **`request`**     | Per request      | Request-scoped coordination  |

### Scope Lock Examples

#### Example 1: Application Counter

```js
// Write lock for incrementing
lock scope="application" timeout=10 type="exclusive" {
    application.visitorCount = ( application.visitorCount ?: 0 ) + 1
}

// Read lock for reading
lock scope="application" timeout=10 type="readonly" {
    count = application.visitorCount
}
```

#### Example 2: Session Data Update

```js
lock scope="session" timeout=10 type="exclusive" {
    session.cart.items.append( newItem )
    session.cart.total += newItem.price
}
```

#### Example 3: Server-Wide Configuration

```js
lock scope="server" timeout=10 type="exclusive" {
    server.maintenanceMode = true
    server.lastUpdate = now()
}
```

{% hint style="warning" %}
**Performance Warning**: Scope locks are **coarse-grained** - they lock the ENTIRE scope. This can become a performance bottleneck in high-traffic applications. Prefer **named locks** with specific names for fine-grained control.
{% endhint %}

### Scope Lock vs Named Lock

```js
// ❌ Coarse: Locks entire application scope
lock scope="application" timeout=10 {
    application.users.brad.lastLogin = now()
}

// ✅ Fine-grained: Locks only this user's data
lock name="user-update-brad" timeout=10 {
    application.users.brad.lastLogin = now()
}
```

## ⚙️ Lock Attributes

Complete reference for all `lock` component attributes:

| Attribute            | Type    | Required | Default       | Description                                                   |
| -------------------- | ------- | -------- | ------------- | ------------------------------------------------------------- |
| **`name`**           | string  | ⚠️ \*    | -             | Lock name (server-wide unique identifier). Cannot be empty.   |
| **`scope`**          | string  | ⚠️ \*    | -             | Scope to lock (`application`, `server`, `session`, `request`) |
| **`timeout`**        | integer | ✅ Yes    | -             | Maximum seconds to wait for lock. `0` = wait forever.         |
| **`type`**           | string  | ❌ No     | `"exclusive"` | Lock type: `"exclusive"` or `"readonly"`                      |
| **`throwOnTimeout`** | boolean | ❌ No     | `true`        | Throw `LockException` on timeout?                             |

**\* Mutually Exclusive**: Must provide either `name` OR `scope`, but not both.

### Attribute Details

#### `name` - Lock Name

* **Server-wide** unique identifier
* **Case-sensitive**
* Can use expressions: `name="lock-#userId#"`
* Cannot be empty string
* Mutually exclusive with `scope`

#### `scope` - Scope Name

* Values: `application`, `server`, `session`, `request`
* Locks entire scope
* Mutually exclusive with `name`

#### `timeout` - Wait Time

* **Positive integer**: Wait N seconds for lock
* **Zero (0)**: Wait forever (until lock acquired)
* If lock not acquired within timeout, behavior depends on `throwOnTimeout`

{% hint style="info" %}
**Timeout Best Practice**: Always use the **minimum timeout** necessary. Large timeouts can block threads and reduce throughput.
{% endhint %}

#### `type` - Lock Type

* **`"exclusive"`** (default): Single-thread access (write lock)
* **`"readonly"`**: Multi-thread access (read lock)
* Case-insensitive

#### `throwOnTimeout` - Timeout Behavior

* **`true`** (default): Throws `LockException` if timeout reached
* **`false`**: Skips lock body silently and continues execution

### Attribute Examples

```js
// Exclusive lock with exception on timeout
lock name="mylock" timeout=10 type="exclusive" throwOnTimeout=true {
    // Single-thread access
}

// Readonly lock, skip on timeout
lock name="mylock" timeout=5 type="readonly" throwOnTimeout=false {
    // Multi-thread access, skip if busy
}

// Wait forever for exclusive lock
lock name="critical-section" timeout=0 type="exclusive" {
    // Will wait indefinitely
}

// Dynamic lock name
lock name="user-#userId#" timeout=10 {
    // Per-user lock
}

// Scope lock with readonly
lock scope="application" timeout=10 type="readonly" {
    // Read application data
}
```

## 🌐 Distributed Cache Locking

**New in 1.10.0**: BoxLang provides distributed locking capabilities through the `ILockableCacheProvider` interface, enabling synchronized access to cache entries across multiple servers or application instances.

### When to Use Distributed Cache Locks

Distributed cache locks are essential when:

* ✅ Running multiple application servers behind a load balancer
* ✅ Preventing duplicate processing across server instances
* ✅ Coordinating access to shared cache entries
* ✅ Ensuring atomic operations in clustered environments
* ✅ Managing distributed rate limiting or quotas

### ILockableCacheProvider Interface

Cache providers implementing the `ILockableCacheProvider` interface support distributed locking:

```java
// Check if cache supports locking
cacheInstance = cache( "myDistributedCache" )
if ( cacheInstance.hasInterface( "ILockableCacheProvider" ) ) {
    println( "Cache supports distributed locking" )
}
```

### Using Cache Locks

#### Basic Cache Lock Syntax

```js
// Lock a cache entry for exclusive access
lock cache="myDistributedCache" key="user:123" timeout=10 type="exclusive" {
    // Synchronized access across all servers
    user = cache( "myDistributedCache" ).get( "user:123" ).orElse( {} )
    user.lastAccess = now()
    cache( "myDistributedCache" ).set( "user:123", user )
}
```

#### Readonly Cache Locks

```js
// Multiple servers can read simultaneously
lock cache="myDistributedCache" key="config:app" timeout=5 type="readonly" {
    config = cache( "myDistributedCache" ).get( "config:app" ).get()
    // Read-only access - multiple servers can enter this block
    processConfig( config )
}
```

### Real-World Distributed Lock Examples

#### Example 1: Distributed Job Processing

Prevent duplicate job execution across multiple servers:

```js
function processJob( jobId ) {
    lockKey = "job-lock:#jobId#"

    lock cache="distributed" key=lockKey timeout=300 type="exclusive" throwOnTimeout=false {
        // Check if job already processed
        status = cache( "distributed" ).get( "job-status:#jobId#" )
        if ( status.isPresent() && status.get() == "completed" ) {
            return // Another server already processed this
        }

        // Mark as in-progress
        cache( "distributed" ).set( "job-status:#jobId#", "processing", 300 )

        try {
            // Process the job
            result = performExpensiveOperation( jobId )

            // Mark as completed
            cache( "distributed" ).set( "job-status:#jobId#", "completed", 3600 )
            cache( "distributed" ).set( "job-result:#jobId#", result, 3600 )
        } catch ( any e ) {
            cache( "distributed" ).set( "job-status:#jobId#", "failed", 3600 )
            rethrow
        }
    }
}
```

#### Example 2: Distributed Rate Limiting

Limit API requests across all servers:

```js
function checkRateLimit( userId ) {
    lockKey = "ratelimit:#userId#"
    countKey = "requests:#userId#"

    lock cache="distributed" key=lockKey timeout=5 type="exclusive" {
        // Get current request count
        requestCount = cache( "distributed" ).get( countKey ).orElse( 0 )

        if ( requestCount >= 100 ) {
            throw( type="RateLimitExceeded", message="Too many requests" )
        }

        // Increment counter
        cache( "distributed" ).set( countKey, requestCount + 1, 3600 )
    }
}
```

#### Example 3: Distributed Cache Warmup

Ensure only one server performs expensive cache population:

```js
function getExpensiveData() {
    cacheKey = "expensive-data"
    lockKey = "lock:expensive-data"

    // Try to get from cache first
    data = cache( "distributed" ).get( cacheKey )
    if ( data.isPresent() ) {
        return data.get()
    }

    // Lock to prevent multiple servers from loading simultaneously
    lock cache="distributed" key=lockKey timeout=30 type="exclusive" {
        // Double-check after acquiring lock
        data = cache( "distributed" ).get( cacheKey )
        if ( data.isPresent() ) {
            return data.get()
        }

        // Load expensive data (only one server does this)
        data = performExpensiveQuery()

        // Cache for all servers
        cache( "distributed" ).set( cacheKey, data, 3600 )

        return data
    }
}
```

### Cache Lock Attributes

| Attribute        | Type    | Required | Description                                  |
| ---------------- | ------- | -------- | -------------------------------------------- |
| `cache`          | string  | Yes      | Name of the lockable cache provider          |
| `key`            | string  | Yes      | Cache key to lock                            |
| `type`           | string  | Yes      | Lock type: `"exclusive"` or `"readonly"`     |
| `timeout`        | numeric | Yes      | Maximum seconds to wait for lock             |
| `throwOnTimeout` | boolean | No       | Throw exception on timeout (default: `true`) |

### Cache Lock Requirements

{% hint style="danger" %}
**Cache Provider Must Implement ILockableCacheProvider**

Not all cache providers support distributed locking. Ensure your cache provider implements the `ILockableCacheProvider` interface. If it doesn't, a `CacheException` will be thrown.
{% endhint %}

### Best Practices for Distributed Cache Locks

1. **Use Short Timeouts**: Distributed locks should be fast - keep timeouts under 30 seconds
2. **Handle Timeout Gracefully**: Use `throwOnTimeout=false` for non-critical operations
3. **Lock Specific Keys**: Lock the smallest scope possible (specific cache keys, not entire caches)
4. **Avoid Nested Cache Locks**: Don't lock multiple cache keys in nested blocks (deadlock risk)
5. **Use Readonly When Possible**: Readonly locks allow concurrent access for read operations
6. **Monitor Lock Contention**: Track lock wait times and failures in production
7. **Set Appropriate TTLs**: Cache entries should have appropriate expirations

### Performance Considerations

* **Network Overhead**: Distributed locks require network communication
* **Lock Latency**: Expect 1-50ms overhead depending on network and cache provider
* **Contention**: High contention on popular keys can create bottlenecks
* **Failover**: Ensure cache provider supports proper lock release on server failure

{% hint style="success" %}
**Tip**: For high-throughput scenarios, consider cache sharding or optimistic locking patterns to reduce lock contention.
{% endhint %}

## 🔄 Double-Check Locking Pattern

The **double-check locking** pattern prevents race conditions where multiple threads check a condition, then compete to initialize a resource.

### The Problem: Race Condition

```js
// ❌ Bad: Race condition - multiple threads can initialize
function loadData() {
    myData = cache.get( "mykey" )

    if ( isNull( myData ) ) {
        // Multiple threads pass this check
        lock name="cache-load" timeout=10 type="exclusive" {
            // ALL waiting threads will execute this!
            myData = expensiveOperation()  // Called multiple times!
            cache.set( "mykey", myData )
        }
    }

    return myData
}
```

**Problem**: If 10 threads are waiting at the lock, all 10 will execute the expensive operation!

### The Solution: Double-Check Pattern

```js
// ✅ Good: Double-check prevents race condition
function loadData() {
    myData = cache.get( "mykey" )

    if ( isNull( myData ) ) {
        lock name="cache-load" timeout=10 type="exclusive" {
            // Double-check INSIDE the lock
            myData = cache.get( "mykey" )
            if ( isNull( myData ) ) {
                // Only ONE thread executes this
                myData = expensiveOperation()
                cache.set( "mykey", myData )
            }
        }
    }

    return myData
}
```

### Double-Check Pattern Diagram

{% @mermaid/diagram content="sequenceDiagram
participant T1 as Thread 1
participant T2 as Thread 2
participant Cache
participant Lock

```
T1->>Cache: Check key (not found)
T2->>Cache: Check key (not found)
T1->>Lock: Acquire lock ✓
T2->>Lock: Wait in queue...
T1->>Cache: Double-check (still not found)
T1->>T1: Expensive operation
T1->>Cache: Set value
T1->>Lock: Release lock
T2->>Lock: Acquire lock ✓
T2->>Cache: Double-check (NOW FOUND!)
T2->>Cache: Return cached value
T2->>Lock: Release lock

Note over T1,T2: Only Thread 1 did expensive operation!" %}
```

### Real-World Examples

#### Example 1: Cache Population

```js
function getConfig() {
    config = cache.get( "app-config" )

    if ( isNull( config ) ) {
        lock name="config-load" timeout=10 type="exclusive" {
            // Double-check inside lock
            config = cache.get( "app-config" )
            if ( isNull( config ) ) {
                // Load from database (expensive)
                config = db.query( "SELECT * FROM config" )
                // Parse and transform (expensive)
                config = parseAndValidate( config )
                // Cache for 1 hour
                cache.set( "app-config", config, 3600 )
            }
        }
    }

    return config
}
```

#### Example 2: Singleton Initialization

```js
component singleton {
    property name="instance"

    function getInstance() {
        if ( isNull( variables.instance ) ) {
            lock name="singleton-init" timeout=10 type="exclusive" {
                // Double-check
                if ( isNull( variables.instance ) ) {
                    variables.instance = new ExpensiveObject()
                }
            }
        }
        return variables.instance
    }
}
```

#### Example 3: Application Initialization

```js
function onApplicationStart() {
    if ( !structKeyExists( application, "initialized" ) ) {
        lock scope="application" timeout=30 type="exclusive" {
            // Double-check
            if ( !structKeyExists( application, "initialized" ) ) {
                application.datasource = configureDatasource()
                application.cache = initializeCache()
                application.services = loadServices()
                application.initialized = true
            }
        }
    }
}
```

### Why Double-Check Works

1. **First Check** (outside lock): Fast path - avoids lock overhead when value exists
2. **Second Check** (inside lock): Prevents race condition - ensures only one thread initializes
3. **Lock Protection**: Guarantees atomic initialization

{% hint style="success" %}
**Best Practice**: Always use double-check locking when initializing expensive resources that multiple threads might try to create simultaneously!
{% endhint %}

## ⚠️ Deadlock Prevention

A **deadlock** occurs when threads are waiting for locks held by each other, creating a cycle that cannot be broken.

### Deadlock Example

```js
// Thread 1
lock name="lockA" timeout=10 {
    lock name="lockB" timeout=10 {
        // Do work
    }
}

// Thread 2 - DEADLOCK!
lock name="lockB" timeout=10 {
    lock name="lockA" timeout=10 {
        // Never executes - waiting forever
    }
}
```

### Deadlock Diagram

{% @mermaid/diagram content="graph TD
T1\[Thread 1] -->|Holds| LA\[Lock A]
T1 -->|Waits for| LB\[Lock B]
T2\[Thread 2] -->|Holds| LB
T2 -->|Waits for| LA

```
style T1 fill:#ffcccc
style T2 fill:#ffcccc
style LA fill:#ffeecc
style LB fill:#ffeecc" %}
```

### Deadlock Prevention Strategies

#### 1. Consistent Lock Ordering

Always acquire locks in the **same order** across all code:

```js
// ✅ Good: Consistent ordering everywhere
function operation1() {
    lock name="lockA" timeout=10 {
        lock name="lockB" timeout=10 {
            // Work
        }
    }
}

function operation2() {
    // SAME ORDER: A then B
    lock name="lockA" timeout=10 {
        lock name="lockB" timeout=10 {
            // Work
        }
    }
}
```

#### 2. Use Minimal Timeout

Always set reasonable timeouts so locks auto-release:

```js
// ✅ Good: Timeout prevents infinite deadlock
lock name="mylock" timeout=10 {
    // Will timeout and throw exception after 10 seconds
}

// ❌ Bad: Infinite wait
lock name="mylock" timeout=0 {
    // Could wait forever if deadlocked
}
```

#### 3. Avoid Nested Locks

Simplest solution - don't nest locks:

```js
// ✅ Better: Separate, non-nested locks
lock name="operation1" timeout=10 {
    doWork1()
}

lock name="operation2" timeout=10 {
    doWork2()
}
```

#### 4. Use Lock Hierarchies

Establish a hierarchy and always lock from top to bottom:

```js
// Hierarchy: Global -> Application -> User
lock name="global-config" timeout=10 {
    lock name="app-#appId#-config" timeout=10 {
        lock name="user-#userId#-prefs" timeout=10 {
            // Work
        }
    }
}
```

### Deadlock Recovery

BoxLang's locks automatically release on:

* ✅ **Timeout**: Lock acquisition times out
* ✅ **Exception**: Thread throws exception
* ✅ **Thread Termination**: Thread ends abnormally

```js
try {
    lock name="mylock" timeout=10 throwOnTimeout=true {
        riskyOperation()
    }
} catch ( LockException e ) {
    // Timeout occurred - lock automatically released
    logError( "Lock timeout: #e.lockName#" )
    // Attempt recovery or retry
}
```

### Deadlock Best Practices

✅ **Always use timeouts** - Never use `timeout=0` in production ✅ **Consistent ordering** - Document and enforce lock order ✅ **Minimize lock scope** - Lock smallest code section possible ✅ **Avoid nesting** - Use single locks when possible ✅ **Use named locks** - More precise than scope locks ✅ **Log lock acquisitions** - Track lock usage patterns ✅ **Monitor lock times** - Identify bottlenecks

{% hint style="danger" %}
**Critical**: BoxLang uses Java's `ReentrantReadWriteLock` internally, which **automatically releases locks** on timeout or thread termination, preventing infinite deadlocks. However, large timeouts can still block threads and reduce throughput!
{% endhint %}

## ☕ Advanced: Java Interop Locking

BoxLang's full Java interop allows you to use **native Java synchronization** mechanisms directly for advanced locking scenarios.

### Using Java ReentrantLock

Java's `ReentrantLock` provides explicit lock/unlock control:

```js
// Create a Java ReentrantLock
lock = new java:java.util.concurrent.locks.ReentrantLock()

try {
    // Acquire lock
    lock.lock()

    // Critical section
    application.counter += 1

} finally {
    // ALWAYS unlock in finally
    lock.unlock()
}
```

### Using Java ReentrantReadWriteLock

BoxLang's `lock` component uses this internally:

```js
// Create read/write lock
rwLock = new java:java.util.concurrent.locks.ReentrantReadWriteLock()
readLock = rwLock.readLock()
writeLock = rwLock.writeLock()

// Multiple readers
readLock.lock()
try {
    data = application.config
} finally {
    readLock.unlock()
}

// Single writer
writeLock.lock()
try {
    application.config = newConfig
} finally {
    writeLock.unlock()
}
```

### Using Java Synchronized Blocks

Java's `synchronized` keyword via BoxLang:

```js
// Create a monitor object
monitor = new java:java.lang.Object()

// Synchronized block
synchronized( monitor ) {
    // Only one thread can execute this
    criticalOperation()
}
```

{% hint style="info" %}
**Note**: BoxLang doesn't have native `synchronized` keyword, but you can call Java synchronized methods or use Java lock objects directly.
{% endhint %}

### Using Java Semaphores

Control access with permits:

```js
// Create semaphore with 3 permits
semaphore = new java:java.util.concurrent.Semaphore( 3 )

try {
    // Acquire permit (blocks if none available)
    semaphore.acquire()

    // Max 3 threads execute simultaneously
    doWork()

} finally {
    // Release permit
    semaphore.release()
}
```

### Using Java CountDownLatch

Wait for multiple threads to complete:

```js
// Create latch for 5 threads
latch = new java:java.util.concurrent.CountDownLatch( 5 )

// Each thread counts down
thread action="run" {
    doWork()
    latch.countDown()
}

// Main thread waits for all 5
latch.await()
println( "All threads completed!" )
```

### Using Java CyclicBarrier

Synchronize threads at a barrier point:

```js
// Create barrier for 3 threads
barrier = new java:java.util.concurrent.CyclicBarrier( 3 )

thread action="run" {
    doPhase1()
    barrier.await()  // Wait for others
    doPhase2()
}
```

### Using Java Atomic Variables

Lock-free atomic operations:

```js
// Atomic integer
counter = new java:java.util.concurrent.atomic.AtomicInteger( 0 )

// Thread-safe increment
counter.incrementAndGet()
counter.addAndGet( 5 )

// Compare and set
counter.compareAndSet( 0, 1 )

// Atomic reference
ref = new java:java.util.concurrent.atomic.AtomicReference()
ref.set( myObject )
current = ref.get()
```

### Using Java ConcurrentHashMap

Thread-safe map without explicit locks:

```js
// Create thread-safe map
map = new java:java.util.concurrent.ConcurrentHashMap()

// Thread-safe operations
map.put( "key", "value" )
value = map.get( "key" )
map.putIfAbsent( "key", "default" )
map.computeIfAbsent( "key", () -> expensiveComputation() )
```

### Advanced Pattern: Custom Lock Manager

```js
component {
    property name="locks" default="#new java:java.util.concurrent.ConcurrentHashMap()#"

    function getLock( name ) {
        return locks.computeIfAbsent(
            name,
            () -> new java:java.util.concurrent.locks.ReentrantLock()
        )
    }

    function withLock( name, callback ) {
        lock = getLock( name )
        lock.lock()
        try {
            return callback()
        } finally {
            lock.unlock()
        }
    }
}

// Usage
lockManager = new LockManager()
result = lockManager.withLock( "mylock", () -> {
    return expensiveOperation()
} )
```

### Comparison: BoxLang vs Java Locking

| Feature              | BoxLang `lock`      | Java Locks           | Use When                     |
| -------------------- | ------------------- | -------------------- | ---------------------------- |
| **Syntax**           | Simple, declarative | Explicit lock/unlock | BoxLang: Simplicity          |
| **Auto-release**     | ✅ Automatic         | ❌ Manual finally     | BoxLang: Safety              |
| **Timeout**          | ✅ Built-in          | Manual               | BoxLang: Deadlock prevention |
| **Read/Write**       | ✅ Built-in types    | Manual handling      | BoxLang: Simplicity          |
| **Advanced Control** | ❌ Limited           | ✅ Full control       | Java: Fine-grained           |
| **Lock-free**        | ❌ No                | ✅ Atomics            | Java: Performance            |
| **Semaphores**       | ❌ No                | ✅ Yes                | Java: Permits                |
| **Barriers**         | ❌ No                | ✅ Yes                | Java: Coordination           |

{% hint style="success" %}
**Best Practice**: Use BoxLang's `lock` component for most cases. It's safer (auto-releases), simpler (declarative), and has built-in timeouts. Use Java locks only when you need advanced features like semaphores, barriers, or lock-free atomic operations.
{% endhint %}

### Java Interop Examples

#### Example 1: Rate Limiting with Semaphore

```js
component {
    property name="semaphore" default="#new java:java.util.concurrent.Semaphore( 10 )#"

    function processRequest() {
        acquired = semaphore.tryAcquire( 5, createObject("java", "java.util.concurrent.TimeUnit").SECONDS )

        if ( acquired ) {
            try {
                return handleRequest()
            } finally {
                semaphore.release()
            }
        } else {
            throw( "Rate limit exceeded" )
        }
    }
}
```

#### Example 2: Atomic Counter

```js
component singleton {
    property name="requestCount" default="#new java:java.util.concurrent.atomic.AtomicLong()#"

    function incrementAndGet() {
        return requestCount.incrementAndGet()
    }

    function get() {
        return requestCount.get()
    }
}
```

#### Example 3: Thread-Safe Cache

```js
component {
    property name="cache" default="#new java:java.util.concurrent.ConcurrentHashMap()#"

    function get( key ) {
        return cache.computeIfAbsent( key, ( k ) -> loadFromDatabase( k ) )
    }

    function set( key, value ) {
        cache.put( key, value )
    }
}
```

## 💡 Best Practices

### 1. Use Minimum Timeouts

```js
// ❌ Bad: Large timeout blocks threads
lock name="mylock" timeout=60 {
    quickOperation()
}

// ✅ Good: Minimal timeout
lock name="mylock" timeout=2 {
    quickOperation()
}
```

### 2. Prefer Named Locks Over Scope Locks

```js
// ❌ Bad: Locks entire application scope
lock scope="application" timeout=10 {
    application.users.brad.status = "active"
}

// ✅ Good: Fine-grained named lock
lock name="user-status-brad" timeout=10 {
    application.users.brad.status = "active"
}
```

### 3. Always Use Double-Check Pattern

```js
// ✅ Good: Prevents race conditions
if ( condition ) {
    lock name="init" timeout=10 {
        if ( condition ) {  // Double-check
            initialize()
        }
    }
}
```

### 4. Use Descriptive Lock Names

```js
// ❌ Bad: Generic names
lock name="lock1" timeout=10 { }

// ✅ Good: Descriptive names
lock name="cache-user-#userId#-profile" timeout=10 { }
```

### 5. Handle Timeouts Appropriately

```js
try {
    lock name="mylock" timeout=5 throwOnTimeout=true {
        criticalOperation()
    }
} catch ( LockException e ) {
    logError( "Lock timeout on: #e.lockName#" )
    // Fallback or retry
}
```

### 6. Use Readonly Locks for Read Operations

```js
// ✅ Good: Multiple readers allowed
lock name="config-read" type="readonly" timeout=10 {
    setting = application.config.get( "key" )
}

// Exclusive only when writing
lock name="config-write" type="exclusive" timeout=10 {
    application.config.set( "key", value )
}
```

### 7. Keep Lock Scope Minimal

```js
// ❌ Bad: Large lock scope
lock name="mylock" timeout=10 {
    prepareData()       // Doesn't need lock
    criticalSection()   // Needs lock
    cleanup()           // Doesn't need lock
}

// ✅ Good: Minimal lock scope
prepareData()
lock name="mylock" timeout=10 {
    criticalSection()   // Only lock critical section
}
cleanup()
```

### 8. Document Lock Dependencies

```js
/**
 * Updates user profile
 *
 * Locks required:
 * - user-profile-{userId} (exclusive)
 * - cache-user-{userId} (exclusive)
 *
 * Lock order: profile then cache (prevent deadlock)
 */
function updateUserProfile( userId, data ) {
    lock name="user-profile-#userId#" timeout=10 {
        lock name="cache-user-#userId#" timeout=10 {
            // Update logic
        }
    }
}
```

### 9. Monitor Lock Performance

```js
startTime = getTickCount()
lock name="mylock" timeout=10 {
    operation()
}
duration = getTickCount() - startTime

if ( duration > 1000 ) {
    logWarning( "Lock held for #duration#ms - potential bottleneck" )
}
```

### 10. Use Java Locks for Advanced Scenarios

```js
// ✅ When you need lock-free atomics
counter = new java:java.util.concurrent.atomic.AtomicInteger()

// ✅ When you need semaphores
limiter = new java:java.util.concurrent.Semaphore( 100 )

// ❌ For basic locking, use BoxLang lock component
lock name="simple" timeout=10 { }
```

## 📋 Summary

BoxLang locking provides powerful thread synchronization:

✅ **Two Lock Types** - Exclusive (write) and Readonly (read)

✅ **Named Locks** - Server-wide locks by name

✅ **Scope Locks** - Lock entire BoxLang scopes

✅ **Automatic Release** - Locks release on timeout/exception

✅ **Timeout Control** - Prevent infinite deadlocks

✅ **ReentrantReadWriteLock** - High-performance Java locks internally

✅ **Java Interop** - Full access to Java concurrency APIs

✅ **Deadlock Prevention** - Built-in timeout mechanisms

{% hint style="success" %}
**Pro Tip**: Use named locks with double-check pattern for most scenarios. Reserve scope locks for coarse-grained operations, and use Java locks only when you need advanced features like semaphores or lock-free atomics!
{% endhint %}

## 🔗 Related Documentation

* [Threading](/boxlang-language/syntax/threading.md) - Concurrent execution
* [Variable Scopes](/boxlang-language/syntax/variable-scopes.md) - Understanding BoxLang scopes
* [Exception Management](/boxlang-language/syntax/exception-management.md) - Handling LockException
* [Java Integration](/boxlang-framework/java-integration.md) - Java interop details
* [Java Concurrency Tutorial](https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html) - Oracle's Java synchronization guide


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://boxlang.ortusbooks.com/boxlang-language/syntax/locking.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
