在Java中,当setter正在工作时,如何同步getter?

9

我正在使用一个单一的静态类来提供一个列表的多线程应用程序。 我希望静态类的getter方法可以自由地工作(彼此不同步),但当setter方法在工作时,我希望所有的getter方法都被锁定并等待setter方法完成任务。 我不想在同时调用getter方法时锁定它们,因为这会大大降低性能。 getter方法每天被调用1,000,000次,而setter方法只需要每天工作一次。

4个回答

10

考虑使用java.util.concurrent.locks.ReadWriteLock的实现,例如ReentrantReadWriteLock (查看javadoc)。

ReadWriteLock维护一对相关锁,一个用于只读操作,一个用于写操作。只要没有写入器,多个读取器线程可以同时持有读取锁。写入锁是互斥的。

您应该使用它来替代synchronized。您的getter方法将获取读取锁,然后在返回时释放锁。

public String getX() {
    Lock readLock = readWriteLock.readLock();
    readLock.lock();
    try {
        return value;
    } finally {
        readLock.unlock();
    }
}

对于设置器方法同样如此,但使用readWriteLock.writeLock()代替。

该类将拥有一个单独的ReentrantReadWriteLock对象, 由每个对象上所有getter和setter共享(或者,如果您愿意,每个getter/setter对都有一个)。

这相当繁琐,但应该能提供良好的并发性能。出于这些原因,只有在真正需要时才应采用此方法,并且这意味着测量您仅使用普通同步时获得的并发性能下降。


6
你的setter可以在每次更新时复制数据,getter可以使用这个副本。这对于setter来说可能非常昂贵,但对于getter影响很小。
然而,同步锁可能需要25到100纳秒的时间。即使你每分钟调用一百万次同步方法,synchronized也可能不会增加足够的延迟。但每秒调用一百万次,就一定会有影响。

好的,如果延迟在纳秒级别,我不介意同步所有方法。感谢您提供的信息。 - Jimmy Page
你的第二段似乎不相关。OP似乎并不担心synchronized本身的开销;相反,他只是不希望对getter的调用必须是顺序的。(我认为你是正确的,他不应该担心;但不用担心的原因是getter应该非常快和便宜,而不是因为synchronized。) - ruakh
synchronized 的成本通常比调用 getter 方法要高得多。如果您的 getter 方法比这个花费更高,那么它就不是一个普通的 getter 方法。 - Peter Lawrey
你正在描述一个CopyOnWriteArrayList,它在每次更新时都会复制数据。使用它。 - user949300

3
我会首先同步所有访问,并且只有在证明其存在性能问题时才进行优化。
要进行优化,您可以使用CopyOnWriteArrayList或ReadWriteLock。如果不更加精确地了解上下文,很难给出一个明确的解决方案。

1
这是使用CopyOnWriteArrayList的典型案例。
“这通常会非常昂贵,但在遍历操作远远多于变异操作时可能比替代方案更有效,并且在您无法或不想同步遍历,但需要防止并发线程之间的干扰时非常有用。”

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接