跟我一起学“仓颉”编程语言-可重入读写锁
本章为大家详细的介绍了仓颉编程语言中可重入读写锁的内容。
·
目录
读锁和写锁是互斥的,多线程可以同时持有读锁对共享资源进行读操作,如果一个线程已经持有读锁,那么其他线程不能获取写锁,直到所有读锁全部被释放。同一时刻,只能有一个线程持有写锁,如果一个线程已经持有写锁,那么其他线程就不能持有写锁,直到写锁被释放。
一、读并发性
package Study
// 导包
import std.sync.*
import std.collection.*
import std.convert.*
import std.time.*
import std.random.Random
main () {
// 创建一个读写锁
let readWrite = ReentrantReadWriteMutex()
// 读锁
let read = readWrite.readMutex
// 写锁
let write = readWrite.writeMutex
let list = ArrayList<Future<Unit>>()
// 创建1个写线程
let writeFuture = spawn {
// 保证其他线程一定先获得读锁
sleep(Duration.second)
synchronized(write) {
println("线程${Thread.currentThread.id}持有写锁")
// 模拟执行任务
sleep(Duration.millisecond * (Random().nextInt64(1000) + 3000))
println("线程${Thread.currentThread.id}释放写锁")
}
}
list.add(writeFuture)
// 创建3个读锁
for (_ in 0..3) {
let readFuture = spawn {
synchronized(read) {
println("线程${Thread.currentThread.id}持有读锁")
// 模拟执行任务
sleep(Duration.millisecond * (Random().nextInt64(1000) + 3000))
println("线程${Thread.currentThread.id}释放读锁")
}
}
list.add(readFuture)
}
for (future in list) {
future.get()
}
}
二、写互斥性
package Study
// 导包
import std.sync.*
import std.collection.*
import std.convert.*
import std.time.*
import std.random.Random
main () {
// 创建一个读写锁
let readWrite = ReentrantReadWriteMutex()
// 读锁
let read = readWrite.readMutex
// 写锁
let write = readWrite.writeMutex
let list = ArrayList<Future<Unit>>()
// 创建1个写线程
let writeFuture1 = spawn {
// 保证其他线程一定先获得读锁
sleep(Duration.second)
synchronized(write) {
println("线程${Thread.currentThread.id}持有写锁")
// 模拟执行任务
sleep(Duration.millisecond * (Random().nextInt64(1000) + 3000))
println("线程${Thread.currentThread.id}释放写锁")
}
}
list.add(writeFuture1)
// 再创建1个写线程
let writeFuture2 = spawn {
// 保证其他线程一定先获得读锁
sleep(Duration.second)
synchronized(write) {
println("线程${Thread.currentThread.id}持有写锁")
// 模拟执行任务
sleep(Duration.millisecond * (Random().nextInt64(1000) + 3000))
println("线程${Thread.currentThread.id}释放写锁")
}
}
list.add(writeFuture2)
// 创建3个读锁
for (_ in 0..3) {
let readFuture = spawn {
// 确保writeFuture1一定可以获得写锁
sleep(Duration.second * 5)
synchronized(read) {
println("线程${Thread.currentThread.id}持有读锁")
// 模拟执行任务
sleep(Duration.millisecond * (Random().nextInt64(1000) + 3000))
println("线程${Thread.currentThread.id}释放读锁")
}
}
list.add(readFuture)
}
for (future in list) {
future.get()
}
}
三、可重入性
可重入读写锁也有可重入性。支持锁降级,不支持锁升级。
支持锁降级:已经持有写锁的线程,可以继续获取读锁。如果一个线程已经有写锁,然后获得了读锁,接着释放写锁,那么该线程持有的是读锁。
不支持锁升级:已经有读锁的线程,不能获取写锁。
package Study
// 导包
import std.sync.*
import std.collection.*
import std.convert.*
import std.time.*
import std.random.Random
class ConfigManager {
private var configData = "初始化配置"
// 创建一个读写锁
let readWrite = ReentrantReadWriteMutex()
// 读锁
let read = readWrite.readMutex
// 写锁
let write = readWrite.writeMutex
// 更新并立即读取配置
func updateAndReadConfig(newData: String) {
// 获取写锁
synchronized (write) {
this.configData = newData
println("线程${Thread.currentThread.id}配置更新: ${this.configData}")
// 在释放写锁前,获取读锁,开始锁降级
read.lock()
}
// 读取配置,此时其他写操纵被阻塞,但是可以与其他读操作并发
println("最新配置: ${this.configData}")
// 释放读锁
read.unlock()
}
func readConfig() {
// 获取读锁
synchronized (read) {
println("线程${Thread.currentThread.id}配置更新: ${this.configData}")
}
}
}
main () {
let configManager = ConfigManager()
let list = ArrayList<Future<Unit>>()
// 创建1个写线程
let writeFuture1 = spawn {
configManager.updateAndReadConfig("新的配置")
}
list.add(writeFuture1)
// 再创建1个写线程
let writeFuture2 = spawn {
// 保证writeFuture1线程一定先获得写锁
sleep(Duration.second)
configManager.updateAndReadConfig("测试配置")
}
list.add(writeFuture2)
// 创建3个读锁
for (_ in 0..3) {
let readFuture = spawn {
// 确保writeFuture1一定可以获得写锁
sleep(Duration.second)
configManager.readConfig()
}
list.add(readFuture)
}
for (future in list) {
future.get()
}
}
四、读写锁模式
在非公平模式下读写锁对线程获取锁的顺序不做保证。在公平模式下,有如下规则:
如果读锁已经被其他线程获得,并且存在线程等待写锁,那么当一个未持有读锁的线程尝试获取读锁时,该线程无法获得读锁并进入等待。
如果在读锁被释放后,会优先唤醒一个等待写锁的线程,而不是等待读锁的线程。如果同时存在多个线程等待写锁,它们之间被唤醒的顺序不做保证。
在写锁被释放后,会优先唤醒所有等待读锁的线程,而不是等待写锁的线程。
五、小结
本章为大家详细的介绍了仓颉编程语言中可重入读写锁的内容,下一章,为大家带来ThreadLocal线程安全的内容。最后,创作不易,如果大家觉得我的文章对学习仓颉服务端开发有帮助的话,就动动小手,点个免费的赞吧!收到的赞越多,我的创作动力也会越大哦,谢谢大家🌹🌹🌹!!!
更多推荐
所有评论(0)