Kotlin ReentrantReadWriteLock在解锁时卡住

Kotlin ReentrantReadWriteLock在解锁时卡住,kotlin,concurrency,deadlock,reentrantreadwritelock,Kotlin,Concurrency,Deadlock,Reentrantreadwritelock,我有一个类,用于获取和释放文件的锁。我使用的customKey类只是一个带有id字符串(id是文件)的ReentrantReadWriteLock。出于某种原因,这只在某些情况下有效,在大多数情况下,它挂起所有东西的解锁-我的调试器一直跟踪它的使用情况,然后就卡住了 我做错了什么?我会得到如果一个线程崩溃,并没有释放它的锁,但这里一个线程试图调用解锁,并没有进一步 这是锁定的方法: override fun acquire(lockId: String?, ownerId: String?, s

我有一个类,用于获取和释放文件的锁。我使用的customKey类只是一个带有id字符串(id是文件)的ReentrantReadWriteLock。出于某种原因,这只在某些情况下有效,在大多数情况下,它挂起所有东西的解锁-我的调试器一直跟踪它的使用情况,然后就卡住了

我做错了什么?我会得到如果一个线程崩溃,并没有释放它的锁,但这里一个线程试图调用解锁,并没有进一步

这是锁定的方法:

override fun acquire(lockId: String?, ownerId: String?, sequence: Long): Boolean
{
    if (lockId != null)
    {
        lockedList.find { customLock -> customLock.Id == lockId }.apply {
            if (this != null) //lock already exists for this ID
            {
                println("Locking file $lockId Existing lock")
                this.writeLock().lock()
                println("Locked file $lockId")
            } else //lock does not exist
            {
                val newLock = CustomLock(lockId)
                lockedList.add(newLock)
                println("Locking file $lockId")
                newLock.writeLock().lock()
                println("Locked file $lockId")
            }
        }
        return true
    } else
    {
        throw InvalidParameterException("ERROR: lockId or ownerId is null!")
    }
}
这是释放的方法:

override fun release(lockId: String?, ownerId: String?)
    {
        if (lockId != null)
        {
            lockedList.find { customLock -> customLock.Id == lockId }.apply {
                if (this != null)
                {
                    println("Unlocking file $lockId")
                    this.writeLock().unlock()
                    if (this.isWriteLocked)
                    {
                        throw Exception("ERROR: Unlocking failed!")
                    }
                } else
                {
                    throw Exception("ERROR: Lock not found!")
                }
            }
        }
    }
请不要费心谈论架构,这是由作业决定的。另外,请忽略ownerId和序列变量

编辑:我尝试只使用一个锁,虽然效率不高,但它确实起了作用,因此@gidds可能有点问题,但ConcurrentHashMap和ConcurrentLinkedQueue(替换列表更简单)都没有解决这个问题

EDIT2:这是我使用ConcurrentHashMap的新类。它仍然不能正常工作,有人能指出我哪里搞砸了吗?谢谢

class LockServer(port: Int) : LockConnector, RemoteException()
{
private val lockedList = ConcurrentHashMap<String, CustomLock>()
private var registry: Registry = LocateRegistry.createRegistry(port)

init
{
    registry.bind(ServiceNames.LockService.toString(), UnicastRemoteObject.exportObject(this, port))
}

/**
 * Method acquire() should block the multiple calls from the clients for each specific lockId string.
 * It means when one client acquires the lock "A" and the "A" is not locked by any other clients,
 * the method should record the lock and return true. If the "A" is already locked by any other client,
 * the method is blocked and continues only after the lock "A" is released.
 * (Note: Return value false is not used in this basic implementation.
 * Parameters ownerId and sequence are also not used in this basic implementation.)
 */
override fun acquire(lockId: String?, ownerId: String?, sequence: Long): Boolean
{
    if (lockId != null)
    {
        lockedList.computeIfPresent(lockId){id, value ->
            println("Locking file $id Existing lock")
            value.writeLock().lock()
            println("Locked file $id")
            return@computeIfPresent value
        }
        lockedList.computeIfAbsent(lockId){
            val newLock = CustomLock(it)
            println("Locking file $lockId")
            newLock.writeLock().lock()
            println("Locked file $lockId")
            return@computeIfAbsent newLock
        }
        return true
    } else
    {
        throw InvalidParameterException("ERROR: lockId or ownerId is null!")
    }
}

/**
 * Method release() should release the lock and unblock all waiting acquire() calls for the same lock.
 * (Note: Parameter ownerId is not used in this basic implementation.)
 */
override fun release(lockId: String?, ownerId: String?)
{
    if (lockId != null)
    {
        lockedList.computeIfPresent(lockId){ id, value ->
            println("Unlocking file $id")
            value.writeLock().unlock()
            println("Unlocked file $id")
            return@computeIfPresent value
        }
    }
}

/**
 * Method stop() unbinds the current server object from the RMI registry and unexports it.
 */
override fun stop()
{
    registry.unbind(ServiceNames.LockService.toString())
}
以及发布:

println("Unlocking $lockId")
        lockedList[lockId]!!.writeLock().unlock()
        println("Unlocked $lockId")

仍然是相同的失败

这可能不是您的问题,但是添加新锁时代码有一个争用条件:如果两个线程尝试锁定同一个(新)文件,它们都可以为该文件创建一个锁。两个锁都会添加到列表中,但之后只会找到第一个锁(这假设列表本身是线程安全的;否则,其中一个add可能会失败、永远循环,或者使列表处于不一致的状态,然后崩溃。)

您可以通过一些同步来解决这个问题。但更好的方法可能是将锁存储在一个(键入锁ID)而不是列表中,并使用原子操作,例如安全地创建一个新锁。(这也会提高渐进性能,因为这样可以避免每次扫描列表。)


另外,就风格而言,在锁上使用
apply()
看起来有点笨拙。(它通常用于自定义新创建的对象。)我认为
let()
在那里更为惯用;您只需在内部将
this
更改为
it
。当然,也可以使用老式的临时变量。

这可能不是您的问题,但在添加新锁时,代码有一个争用条件:如果两个线程尝试锁定相同的锁(新)文件,它们都可以为它创建一个锁。两个锁都会添加到列表中,但之后只会找到第一个锁。(这假设列表本身是线程安全的;否则其中一个添加可能会失败、永远循环,或者使列表处于不一致的状态,稍后崩溃。)

您可以通过一些同步来解决这个问题。但更好的方法可能是将锁存储在一个(键入锁ID)而不是列表中,并使用原子操作,例如安全地创建一个新锁。(这也会提高渐进性能,因为这样可以避免每次扫描列表。)


另外,就风格而言,在锁上使用
apply()
看起来有点笨拙。(它通常用于自定义新创建的对象。)我认为
let()
在那里会更为惯用;您只需在内部将
更改为
。当然,也可以使用老式的临时变量。

这可能不是LockServer类的问题,但正在使用它的类:

线程1:

acquire("file1")
acquire("file2")
release("file2")
release("file1")
线程2:

acquire("file2")
acquire("file1")
release("file1")
release("file2")
碰巧执行命令如下:

thread1.acquire("file1")
thread2.acquire("file2")
thread1.acquire("file2") //locked by thread2, waiting
thread2.acquire("file1") //locked by thread1... BOOM, deadlock!
UPD.:

考虑对现有锁使用
tryLock()
(可能有一些超时)而不是简单的
lock()

fun-tryAcquire(lockId:String?,超时:Long,单位:TimeUnit):布尔值{
if(lockId!=null){
var成功=错误
compute(lockId){id,值->
如果(值==null){
println(“锁定文件$id”)
val newLock=CustomLock(id)
newLock.writeLock().lock()
成功=正确
println(“锁定文件$id”)
return@compute纽洛克
}
println(“锁定文件$id现有锁”)
val lock=value.writeLock()
if(lock.tryLock()| | lock.tryLock(超时,单位)){
成功=正确
println(“锁定文件$id”)
}
return@compute价值
}
回归成功
}否则{
抛出InvalidParameterException(“错误:lockId或ownerId为null!”)
}
}

这可能不是LockServer类的问题,而是使用它的人的问题:

线程1:

acquire("file1")
acquire("file2")
release("file2")
release("file1")
线程2:

acquire("file2")
acquire("file1")
release("file1")
release("file2")
碰巧执行命令如下:

thread1.acquire("file1")
thread2.acquire("file2")
thread1.acquire("file2") //locked by thread2, waiting
thread2.acquire("file1") //locked by thread1... BOOM, deadlock!
UPD.:

考虑对现有锁使用
tryLock()
(可能有一些超时)而不是简单的
lock()

fun-tryAcquire(lockId:String?,超时:Long,单位:TimeUnit):布尔值{
if(lockId!=null){
var成功=错误
compute(lockId){id,值->
如果(值==null){
println(“锁定文件$id”)
val newLock=CustomLock(id)
newLock.writeLock().lock()
成功=正确
println(“锁定文件$id”)
return@compute纽洛克
}
println(“锁定文件$id现有锁”)
val lock=value.writeLock()
if(lock.tryLock()| | lock.tryLock(超时,单位)){
成功=正确
println(“锁定文件$id”)
}
return@compute价值
}
回归成功
}否则{
扔我