0
# Locks and Synchronization
1
2
Multiplatform synchronization primitives including reentrant locks and synchronized blocks, providing thread-safe coordination mechanisms for complex concurrent operations.
3
4
## Capabilities
5
6
### Reentrant Lock Factory
7
8
Creates reentrant locks for advanced synchronization scenarios requiring explicit lock management.
9
10
```kotlin { .api }
11
/**
12
* Creates a new ReentrantLock instance.
13
* On JVM, this is a typealias for java.util.concurrent.locks.ReentrantLock.
14
* @returns New ReentrantLock instance
15
*/
16
fun reentrantLock(): ReentrantLock
17
18
/**
19
* JVM typealias for java.util.concurrent.locks.ReentrantLock
20
*/
21
typealias ReentrantLock = java.util.concurrent.locks.ReentrantLock
22
```
23
24
**Usage Examples:**
25
26
```kotlin
27
import kotlinx.atomicfu.locks.*
28
29
class ConcurrentResourceManager {
30
private val lock = reentrantLock()
31
private val resources = mutableMapOf<String, Any>()
32
33
fun addResource(key: String, resource: Any) {
34
lock.lock()
35
try {
36
resources[key] = resource
37
println("Added resource: $key")
38
} finally {
39
lock.unlock()
40
}
41
}
42
43
fun getResource(key: String): Any? {
44
lock.lock()
45
try {
46
return resources[key]
47
} finally {
48
lock.unlock()
49
}
50
}
51
52
fun removeResource(key: String): Any? {
53
lock.lock()
54
try {
55
return resources.remove(key)
56
} finally {
57
lock.unlock()
58
}
59
}
60
61
fun getAllKeys(): Set<String> {
62
lock.lock()
63
try {
64
return resources.keys.toSet()
65
} finally {
66
lock.unlock()
67
}
68
}
69
}
70
```
71
72
### ReentrantLock with Lock Extension
73
74
Convenient extension function for automatic lock management with proper cleanup.
75
76
```kotlin { .api }
77
/**
78
* Executes the given block while holding the lock, automatically releasing it afterwards.
79
* @param block - Code block to execute while holding the lock
80
* @returns Result of the block execution
81
*/
82
fun <T> ReentrantLock.withLock(block: () -> T): T
83
```
84
85
**Usage Examples:**
86
87
```kotlin
88
import kotlinx.atomicfu.locks.*
89
90
class ThreadSafeCounter {
91
private val lock = reentrantLock()
92
private var count = 0
93
94
fun increment(): Int = lock.withLock {
95
count++
96
count
97
}
98
99
fun decrement(): Int = lock.withLock {
100
count--
101
count
102
}
103
104
fun get(): Int = lock.withLock { count }
105
106
fun addAndGet(delta: Int): Int = lock.withLock {
107
count += delta
108
count
109
}
110
111
fun reset(): Int = lock.withLock {
112
val oldValue = count
113
count = 0
114
oldValue
115
}
116
}
117
118
// Complex example with multiple operations
119
class BankAccount(private val initialBalance: Double) {
120
private val lock = reentrantLock()
121
private var balance = initialBalance
122
private val transactions = mutableListOf<String>()
123
124
fun deposit(amount: Double): Double = lock.withLock {
125
require(amount > 0) { "Deposit amount must be positive" }
126
balance += amount
127
transactions.add("Deposit: +$amount")
128
balance
129
}
130
131
fun withdraw(amount: Double): Double = lock.withLock {
132
require(amount > 0) { "Withdrawal amount must be positive" }
133
require(balance >= amount) { "Insufficient funds" }
134
balance -= amount
135
transactions.add("Withdrawal: -$amount")
136
balance
137
}
138
139
fun getBalance(): Double = lock.withLock { balance }
140
141
fun getTransactionHistory(): List<String> = lock.withLock {
142
transactions.toList() // Return defensive copy
143
}
144
145
fun transfer(other: BankAccount, amount: Double): Boolean {
146
// Avoid deadlock by ordering locks by object hash
147
val (firstLock, secondLock) = if (this.hashCode() < other.hashCode()) {
148
this.lock to other.lock
149
} else {
150
other.lock to this.lock
151
}
152
153
return firstLock.withLock {
154
secondLock.withLock {
155
if (this.balance >= amount) {
156
this.balance -= amount
157
other.balance += amount
158
this.transactions.add("Transfer out: -$amount")
159
other.transactions.add("Transfer in: +$amount")
160
true
161
} else {
162
false
163
}
164
}
165
}
166
}
167
}
168
```
169
170
### Synchronized Object
171
172
Platform-specific synchronization object for use with synchronized blocks.
173
174
```kotlin { .api }
175
/**
176
* JVM typealias for Any - any object can be used for synchronization
177
*/
178
typealias SynchronizedObject = Any
179
180
/**
181
* Executes the given block synchronized on the specified lock object.
182
* @param lock - Object to synchronize on
183
* @param block - Code block to execute synchronized
184
* @returns Result of the block execution
185
*/
186
fun <T> synchronized(lock: SynchronizedObject, block: () -> T): T
187
```
188
189
**Usage Examples:**
190
191
```kotlin
192
import kotlinx.atomicfu.locks.*
193
194
class SynchronizedDataStore {
195
private val lock = Any() // SynchronizedObject
196
private val data = mutableMapOf<String, String>()
197
198
fun put(key: String, value: String) = synchronized(lock) {
199
data[key] = value
200
}
201
202
fun get(key: String): String? = synchronized(lock) {
203
data[key]
204
}
205
206
fun remove(key: String): String? = synchronized(lock) {
207
data.remove(key)
208
}
209
210
fun size(): Int = synchronized(lock) {
211
data.size
212
}
213
214
fun getAllEntries(): Map<String, String> = synchronized(lock) {
215
data.toMap() // Return defensive copy
216
}
217
218
fun clear() = synchronized(lock) {
219
data.clear()
220
}
221
}
222
223
// Multiple synchronized objects for fine-grained locking
224
class MultiLockCache {
225
private val readLock = Any()
226
private val writeLock = Any()
227
private val cache = mutableMapOf<String, Any>()
228
private var cacheStats = mutableMapOf<String, Int>()
229
230
fun get(key: String): Any? = synchronized(readLock) {
231
val value = cache[key]
232
if (value != null) {
233
synchronized(writeLock) {
234
cacheStats[key] = (cacheStats[key] ?: 0) + 1
235
}
236
}
237
value
238
}
239
240
fun put(key: String, value: Any) = synchronized(writeLock) {
241
cache[key] = value
242
cacheStats[key] = cacheStats[key] ?: 0
243
}
244
245
fun getStats(): Map<String, Int> = synchronized(writeLock) {
246
cacheStats.toMap()
247
}
248
}
249
```
250
251
### ReentrantLock Core Operations
252
253
Direct access to ReentrantLock methods for advanced use cases.
254
255
```kotlin { .api }
256
class ReentrantLock {
257
/** Acquires the lock, blocking if necessary */
258
fun lock()
259
260
/** Attempts to acquire the lock without blocking */
261
fun tryLock(): Boolean
262
263
/** Releases the lock */
264
fun unlock()
265
}
266
```
267
268
**Usage Examples:**
269
270
```kotlin
271
import kotlinx.atomicfu.locks.*
272
import java.util.concurrent.TimeUnit
273
274
class AdvancedLockExample {
275
private val lock = reentrantLock()
276
private var sharedResource: String? = null
277
278
fun tryUpdateResource(newValue: String, timeoutMs: Long): Boolean {
279
val acquired = lock.tryLock(timeoutMs, TimeUnit.MILLISECONDS)
280
if (!acquired) {
281
println("Failed to acquire lock within timeout")
282
return false
283
}
284
285
try {
286
// Simulate some work
287
Thread.sleep(100)
288
sharedResource = newValue
289
println("Resource updated to: $newValue")
290
return true
291
} finally {
292
lock.unlock()
293
}
294
}
295
296
fun getResourceWithTimeout(timeoutMs: Long): String? {
297
val acquired = lock.tryLock(timeoutMs, TimeUnit.MILLISECONDS)
298
if (!acquired) {
299
println("Failed to acquire lock for reading")
300
return null
301
}
302
303
try {
304
return sharedResource
305
} finally {
306
lock.unlock()
307
}
308
}
309
310
fun forceUpdateResource(newValue: String) {
311
lock.lock() // Block until lock is available
312
try {
313
sharedResource = newValue
314
println("Resource force-updated to: $newValue")
315
} finally {
316
lock.unlock()
317
}
318
}
319
}
320
```
321
322
## Advanced Synchronization Patterns
323
324
### Reader-Writer Lock Pattern
325
326
Using multiple locks for reader-writer scenarios:
327
328
```kotlin
329
import kotlinx.atomicfu.locks.*
330
import kotlinx.atomicfu.*
331
332
class ReadWriteCache<K, V> {
333
private val readLock = reentrantLock()
334
private val writeLock = reentrantLock()
335
private val cache = mutableMapOf<K, V>()
336
private val readers = atomic(0)
337
338
fun read(key: K): V? {
339
readLock.withLock {
340
readers.incrementAndGet()
341
}
342
343
try {
344
return cache[key]
345
} finally {
346
readLock.withLock {
347
readers.decrementAndGet()
348
}
349
}
350
}
351
352
fun write(key: K, value: V) {
353
writeLock.withLock {
354
// Wait for all readers to finish
355
while (readers.value > 0) {
356
Thread.yield()
357
}
358
359
cache[key] = value
360
}
361
}
362
363
fun size(): Int = readLock.withLock { cache.size }
364
}
365
```
366
367
### Condition-based Synchronization
368
369
Using locks with condition variables:
370
371
```kotlin
372
import kotlinx.atomicfu.locks.*
373
import kotlinx.atomicfu.*
374
import java.util.concurrent.locks.Condition
375
376
class BlockingQueue<T>(private val capacity: Int) {
377
private val lock = reentrantLock()
378
private val notFull: Condition = lock.newCondition()
379
private val notEmpty: Condition = lock.newCondition()
380
private val queue = ArrayDeque<T>()
381
382
fun put(item: T) = lock.withLock {
383
while (queue.size >= capacity) {
384
notFull.await()
385
}
386
queue.addLast(item)
387
notEmpty.signal()
388
}
389
390
fun take(): T = lock.withLock {
391
while (queue.isEmpty()) {
392
notEmpty.await()
393
}
394
val item = queue.removeFirst()
395
notFull.signal()
396
item
397
}
398
399
fun size(): Int = lock.withLock { queue.size }
400
401
fun isEmpty(): Boolean = lock.withLock { queue.isEmpty() }
402
403
fun isFull(): Boolean = lock.withLock { queue.size >= capacity }
404
}
405
```
406
407
## Implementation Notes
408
409
### JVM Integration
410
411
- `ReentrantLock` is a direct typealias to `java.util.concurrent.locks.ReentrantLock`
412
- `SynchronizedObject` is a typealias to `Any` - any object can be used for synchronization
413
- `synchronized` function maps directly to Kotlin's `kotlin.synchronized`
414
- Full compatibility with existing Java concurrent programming patterns
415
416
### Performance Considerations
417
418
- ReentrantLocks have higher overhead than basic synchronization but offer more features
419
- `withLock` extension provides automatic cleanup and exception safety
420
- Synchronized blocks have lower overhead but less flexibility
421
- Choose based on specific requirements: simplicity vs. advanced features
422
423
### Best Practices
424
425
- Always use `withLock` or proper try/finally blocks for lock management
426
- Avoid holding locks for long periods
427
- Consider lock ordering to prevent deadlocks in multi-lock scenarios
428
- Use `tryLock` with timeouts for robust error handling
429
- Prefer atomic operations over locks when possible for better performance