目录

一、读并发性

二、写互斥性

三、可重入性

四、读写锁模式

五、小结


读锁和写锁是互斥的,多线程可以同时持有读锁对共享资源进行读操作,如果一个线程已经持有读锁,那么其他线程不能获取写锁,直到所有读锁全部被释放。同一时刻,只能有一个线程持有写锁,如果一个线程已经持有写锁,那么其他线程就不能持有写锁,直到写锁被释放。

一、读并发性

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线程安全的内容。最后,创作不易,如果大家觉得我的文章对学习仓颉服务端开发有帮助的话,就动动小手,点个免费的赞吧!收到的赞越多,我的创作动力也会越大哦,谢谢大家🌹🌹🌹!!!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐