11

我对多线程环境有些陌生,我正在尝试为以下情况提出最佳解决方案:

我每天早上从数据库中读取一次数据,并将数据存储在 Singleton 对象的 HashMap 中。我有一个 setter 方法,仅在发生日内数据库更改时调用(每天发生 0-2 次)。

我还有一个 getter,它返回地图中的一个元素,这个方法每天被调用数百次。

我担心在清空和重新创建 HashMap 时调用 getter 的情况,从而试图在空/格式错误的列表中找到一个元素。如果我使这些方法同步,它会阻止两个读取器同时访问 getter,这可能是性能瓶颈。我不想对性能造成太大的影响,因为写入很少发生。如果我使用 ReentrantReadWriteLock,这是否会强制任何调用 getter 的人排队,直到释放写锁?它是否允许多个读者同时访问 getter?它会一次只强制一名作家吗?

编码这只是一个问题......

private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock read = readWriteLock.readLock();
private final Lock write = readWriteLock.writeLock();

public HashMap getter(String a) {
    read.lock();
    try {
        return myStuff_.get(a);            
    } finally {
        read.unlock();
    }
}

public void setter() 
{
    write.lock();
    try {
        myStuff_ = // my logic
     } finally {
          write.unlock();
    }
}
4

3 回答 3

15

实现此目的的另一种方法(不使用锁)是写时复制模式。当您不经常写作时,它会很好地工作。这个想法是复制和替换字段本身。它可能如下所示:

private volatile Map<String,HashMap> myStuff_ = new HashMap<String,HashMap>();

public HashMap getter(String a) {
    return myStuff_.get(a);
}

public synchronized void setter() {
    // create a copy from the original
    Map<String,HashMap> copy = new HashMap<String,HashMap>(myStuff_);
    // populate the copy
    // replace copy with the original
    myStuff_ = copy;
}

With this, the readers are fully concurrent, and the only penalty they pay is a volatile read on myStuff_ (which is very little). The writers are synchronized to ensure mutual exclusion.

于 2011-05-13T05:22:05.327 回答
2

是的,如果一个线程持有写锁,那么访问 getter 方法的其他线程将阻塞,因为它们无法获取读锁。所以你在这里很好。有关更多详细信息,请阅读 ReentrantReadWriteLock 的 JavaDoc - http://download.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html

于 2011-05-12T18:33:16.500 回答
0

您在一天开始时就开始了这件事……您将每天更新 0-2 次,并且每天阅读 100 次。假设阅读要花费,比如说 8 小时一天(28800 秒)中的 1 整秒(一个 looonnnng 时间),你仍然有一个非常低的阅读负载。查看 ReentrantReadWriteLock 的文档,您可以“tweek”该模式以使其“公平”,这意味着等待时间最长的线程将获得锁。因此,如果您将其设置为公平的,我认为您的写入线程不会被饿死。

参考

可重入读写锁

于 2011-05-12T18:38:41.023 回答