首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ReentrantReadWriteLock在解锁时卡住

ReentrantReadWriteLock在解锁时卡住
EN

Stack Overflow用户
提问于 2020-10-06 18:00:34
回答 2查看 121关注 0票数 0

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

我做错了什么?如果一个线程崩溃了,并且没有释放它的锁,我会得到它,但是在这里,一个线程试图调用unlock,并且不再前进。

以下是锁定的方法:

代码语言:javascript
复制
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!")
    }
}

以下是释放的方法:

代码语言:javascript
复制
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和sequence变量。

编辑:我试着只使用一个锁,虽然效率不是很高,但它确实有效,所以@gidds可能有点用,但是ConcurrentHashMap和ConcurrentLinkedQueue (用它替换列表更简单)都没有解决这个问题。

EDIT2:这是我在其中使用ConcurrentHashMap的新类。还是不能正常工作,有没有人能指出我哪里搞砸了?谢谢

代码语言:javascript
复制
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())
}

}

EDIT3: acquire的新实现:

代码语言:javascript
复制
lockedList.compute(lockId){id, value ->
            if (value == null)
            {
                println("Locking file $id")
                val newLock = CustomLock(id)
                newLock.writeLock().lock()
                println("Locked file $id")
                return@compute newLock
            }
            println("Locking file $id Existing lock")
            value.writeLock().lock()
            println("Locked file $id")
            return@compute value
        }

对于发行版:

代码语言:javascript
复制
println("Unlocking $lockId")
        lockedList[lockId]!!.writeLock().unlock()
        println("Unlocked $lockId")

仍然是同样的失败

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-10-08 01:44:00

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

thread1:

代码语言:javascript
复制
acquire("file1")
acquire("file2")
release("file2")
release("file1")

thread2:

代码语言:javascript
复制
acquire("file2")
acquire("file1")
release("file1")
release("file2")

碰巧执行顺序如下:

代码语言:javascript
复制
thread1.acquire("file1")
thread2.acquire("file2")
thread1.acquire("file2") //locked by thread2, waiting
thread2.acquire("file1") //locked by thread1... BOOM, deadlock!

更新:

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

代码语言:javascript
复制
    fun tryAcquire(lockId: String?, timeout: Long, unit: TimeUnit): Boolean {
        if (lockId != null) {
            var success = false
            lockedList.compute(lockId) { id, value ->
                if (value == null) {
                    println("Locking file $id")
                    val newLock = CustomLock(id)
                    newLock.writeLock().lock()
                    success = true
                    println("Locked file $id")
                    return@compute newLock
                }
                println("Locking file $id Existing lock")
                val lock = value.writeLock()
                if (lock.tryLock() || lock.tryLock(timeout, unit)) {
                    success = true
                    println("Locked file $id")
                }
                return@compute value
            }
            return success
        } else {
            throw InvalidParameterException("ERROR: lockId or ownerId is null!")
        }
    }
票数 1
EN

Stack Overflow用户

发布于 2020-10-06 19:06:29

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

你可以通过一些同步来解决这个问题。但是一个更好的方法可能是将锁存储在一个ConcurrentHashMap (锁ID上的键)中,而不是列表中,并使用诸如computeIfAbsent()这样的原子操作来安全地创建一个新锁。

此外,作为一个风格问题,在锁上使用apply()看起来有点笨拙。let() (它通常用于自定义新创建的对象)。我认为在那里let()会更惯用;你只需要在里面将this改为it。当然,或者使用老式的临时变量。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64223538

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档