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.
🎯 Lock Basics
BoxLang provides the lock component/construct for thread-safe synchronization:
// 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
🔐 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.
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.
Characteristics:
✅ Multiple concurrent readers allowed
✅ Blocked by exclusive locks
✅ Use for reading shared data
✅ Better performance for read-heavy workloads
Lock Type Comparison
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
📛 Named Locking
Named locks use a string identifier to coordinate access across the entire BoxLang server.
Basic Named Lock
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.
Naming Conventions
Use descriptive, namespaced lock names to avoid collisions:
Lock Name Characteristics
Case-Sensitive:
"MyLock"and"mylock"are different locksServer-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
Example 2: Counter Increment
Example 3: Multiple Locks with Different Names
🎯 Scope Locking
Scope locks synchronize access to entire BoxLang scopes: application, server, session, request.
Scope Lock Syntax
How Scope Locks Work
Each scope has a unique internal lock name generated from the scope name and instance hash:
This ensures each scope instance (e.g., each session) has its own lock.
Available Scopes
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
Example 2: Session Data Update
Example 3: Server-Wide Configuration
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.
Scope Lock vs Named Lock
⚙️ Lock Attributes
Complete reference for all lock component attributes:
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,requestLocks 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
type - Lock Type
"exclusive"(default): Single-thread access (write lock)"readonly": Multi-thread access (read lock)Case-insensitive
throwOnTimeout - Timeout Behavior
true(default): ThrowsLockExceptionif timeout reachedfalse: Skips lock body silently and continues execution
Attribute Examples
🌐 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:
Using Cache Locks
Basic Cache Lock Syntax
Readonly Cache Locks
Real-World Distributed Lock Examples
Example 1: Distributed Job Processing
Prevent duplicate job execution across multiple servers:
Example 2: Distributed Rate Limiting
Limit API requests across all servers:
Example 3: Distributed Cache Warmup
Ensure only one server performs expensive cache population:
Cache Lock Attributes
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
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.
Best Practices for Distributed Cache Locks
Use Short Timeouts: Distributed locks should be fast - keep timeouts under 30 seconds
Handle Timeout Gracefully: Use
throwOnTimeout=falsefor non-critical operationsLock Specific Keys: Lock the smallest scope possible (specific cache keys, not entire caches)
Avoid Nested Cache Locks: Don't lock multiple cache keys in nested blocks (deadlock risk)
Use Readonly When Possible: Readonly locks allow concurrent access for read operations
Monitor Lock Contention: Track lock wait times and failures in production
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
Tip: For high-throughput scenarios, consider cache sharding or optimistic locking patterns to reduce lock contention.
🔄 Double-Check Locking Pattern
⚠️ Deadlock Prevention
☕ Advanced: Java Interop Locking
💡 Best Practices
📋 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
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!
🔗 Related Documentation
Threading - Concurrent execution
Variable Scopes - Understanding BoxLang scopes
Exception Management - Handling LockException
Java Integration - Java interop details
Java Concurrency Tutorial - Oracle's Java synchronization guide
Last updated
Was this helpful?
