【问题标题】:Reentrant readwrite lock synchronization on isWriteLocked可重入读写锁同步 on is Write Locked
【发布时间】:2015-10-07 22:37:06
【问题描述】:

我目前正在考虑实现一个会话管理器 java 类,该类提供按需读取和刷新会话令牌的功能。如果正在刷新会话令牌(即从服务器获取),则会话令牌读取器应阻塞,直到刷新完成。同样,刷新请求会被阻止,直到任何正在进行的读取都未完成。由于会话令牌读取请求与会话令牌刷新请求相比非常频繁,因此我决定使用 ReentrantReadWriteLock 来实现读取和刷新之间的同步。这是它的外观:

  String refreshToken() {
         try{
             if (readWriteLock.writeLock().trylock()) {
                 //fetch session from server and store on disk
             }
        } finally {
              readWriteLock.writeLock().unlock();  
        }
        return readToken();
    }

   String readToken() {
         try {
         readWriteLock.readLock().lock();
         //read token from disk
         } finally {
               readWriteLock.readLock().unlock();
         }
    return token;
    }

}

我的第一次尝试是在写锁上使用 tryLock(),这样如果它已经被锁定写,tryLock() 将返回 false,然后获取读锁并阻塞,直到写锁被释放,从而重新调度read-blocked 线程来完成读取。此逻辑适用于多个线程同时调用 refreshSession() 从而仅允许一个线程启动会话令牌刷新而所有其他线程失败并阻塞读锁的情况。

然而,如果一个线程刚刚获得了一个读锁(通过调用 readToken())并且另一个线程调用了 refreshToken() 来获得一个写锁,那么上面的逻辑就会失败——在这种情况下 tryLock() 会失败结果刷新请求。

作为替代方案,我正在查看 readWriteLock.isWriteLocked() 方法,该方法检查是否有任何线程获得了写锁:

String refreshToken() {
    try{
        if (!readWriteLock.isWriteLocked()) {
            readWriteLock.writeLock().lock();
            //fetch session from server and store on disk
        } finally{
              readWriteLock.writeLock().unlock();  
        }
    }
    return readToken();
}

但是,我对这种方法没有太多经验,也不完全确定它会产生的同步影响,因为我想确保只有一个线程可以获得写锁,并且后续请求会被读取。 任何建议/指针将不胜感激。

【问题讨论】:

  • 使refreshToken() 成为synchronized 方法可以完成这项工作,然后一次只有一个线程可以访问refreshToken() 部分。其他线程将等待当前“在”方法中的线程完成
  • 好吧,我想在读者和作者之间同步对会话令牌的访问。此外,类上还会有其他方法,所以我不想通过使这个方法同步来阻塞整个对象。因此使用锁的原因。

标签: java multithreading concurrency locking reentrantreadwritelock


【解决方案1】:

您可以在那里引入新的ReentrantLocktryLock。 ReentrantLock 只承诺一个写入器,如果存在写入器,则会很快失败。

ReentrantLock lock = new ReentrantLock();

public String refreshToken() {
    if (lock.tryLock()) {
        readWriteLock.writeLock().lock();
        try {
            // fetch session from server and store on disk
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }
    return readToken();
}

所以只有一个线程会尝试写入。并且在写入过程中没有线程可以读取。

【讨论】:

  • 看起来很有希望——还有其他不需要额外锁定的想法吗?
  • 在没有任何进一步的 cmets/建议的情况下,我接受这个答案。
  • @user1623182 谢谢,关于您之前的评论,您有什么想法?在不引入某种形式的互斥体的情况下,我不太明白如何实现你想要的。例如,您的示例不会无限期地保护,想象两个线程同时调用 isWriteLocked 并且都成功。在这种情况下,两者都将调用lock
  • 你说得对,我对直接使用 isWriteLocked 而不使用任何互斥锁有同样的保留。但是,我只是在检查您是否有任何其他想法/方法,而无需使用两个锁。考虑到 API 要求(如最初概述的那样),您认为我采用的会话刷新策略是最优的吗?
  • 我的意思是使用我可能不知道的任何其他原语或并发 API。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-13
  • 2019-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多