【问题标题】:correct usage of readwrite locks正确使用读写锁
【发布时间】:2018-12-18 08:47:29
【问题描述】:

我有如下要求:

1) 具有所有静态方法和静态列表的类。此列表存储了一些我对其执行某些操作的对象。

2) 现在这个操作是从多个线程调用的。

3) 此操作调用不共享任何公共数据,因此此方法不同步。

4) 现在,每当这个列表被新对象更新时,我必须停止这个操作调用。

class StaticClass{
    static List<SomeObjects>=new List<>();
    static performOperation()
    {
        //call operation on all objects in the list
    }

    static updateList()
    {
        //update list, add, update or remove objects 
    }
}

可能的解决方案:

1) 使 performOperation() 和 updateList() 同步。但是 performOperation() 被调用的频率太高,而更新列表的频率太低。

2) 使用读写锁。在 performOperation() 中使用读锁,在 updateList() 中使用写锁。示例如下:

class StaticClass{
    static List<SomeObjects>=new List<>();
    static final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    static performOperation()
    {
        readWriteLock.readLock().lock();
        //call operation on all objects in the list
        readWriteLock.readLock().unlock();
    }

    static updateList()
    {
        readWriteLock.writeLock().lock();
        //update list, add, update or remove objects 
        readWriteLock.writeLock().unlock();
    }

那么哪种解决方案更好?这是读写锁的正确用法吗?为什么我对方法 2 感到困惑是 performOperation() 中没有需要读访问或写访问的数据。我只是在更新列表时无法调用此方法。所以我不确定它是否适合使用读写锁。

【问题讨论】:

  • 我不明白你为什么不使用synchronized。您对读写锁的使用是正确的,但是在这种情况下您不会获得太多(什么?)。读写锁主要在有多个读取器时提供好处,因为它们允许所有读取器同时读取。
  • Re,“这个操作调用不共享任何公共数据......”你的意思是什么。列表本身数据,并且列表是共享的。你是说performOperation() 没有更新任何共享数据吗?你是说没有两个线程会更新同一个对象?
  • 是的 @jameslarge performOperations() 是线程安全的。他们从不对列表执行任何更新。基本上他们只是对存储在列表中的对象调用一些方法。
  • @Michael performOperations() 是线程安全的。他们从不对列表执行任何操作。基本上他们只是对存储在列表中的对象调用一些方法。由于它没有更新任何共享数据,我想保持它不同步,以便所有线程可以并行调用它。

标签: java multithreading locking readwritelock


【解决方案1】:

ReadWriteLock 在发生大量读取时效率更高,因为synchronized 会阻止所有内容。也就是说ReadWriteLock 更容易出错。例如,您的示例实际上会陷入死锁,因为每次调用 readWriteLock.writeLock()readWriteLock.readLock() 时,它都会创建一个新实例,并且永远不会解锁,导致它最终陷入死锁。所以你的例子应该看起来更像:

class StaticClass{
    static List<SomeObjects>=new List<>();
    static final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    static final Lock readLock = readWriteLock.readLock();
    static final Lock writeLock = readWriteLock.writeLock();

    static void performOperation()
    {
        readLock.lock();
        try {
            //call operation on all objects in the list
        } finally {
            // This ensures read lock is unlocked even when exception occurs
            readLock.unlock();
        }
    }

    static void updateList()
    {
        writeLock.lock();
        try {
            //update list, add, update or remove objects 
        } finally {
            // This ensures read lock is unlocked even when exception occurs
            writeLock.unlock();
        }
    }
}

请注意,我还在此处添加了try/finally,以避免可能出现的异常问题。如您所见,这比简单的synchronized 部分要多得多。

还有一个可能的替代方案CopyOnWriteArrayList。这是线程安全的,您不必使用锁或synchronized 关键字。当有大量写入时,它会影响您的性能。

【讨论】:

  • public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; } public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
  • 现在如果你看一下这个获取读写锁的实现,它似乎并不是每次都创建一个新的锁。您最终的想法很好,但我想拥有 readLock 和 writeLock 不会带来任何好处
猜你喜欢
  • 2017-02-03
  • 1970-01-01
  • 1970-01-01
  • 2011-01-25
  • 2011-12-04
  • 2015-08-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-24
相关资源
最近更新 更多