更新缓存的一个设计方案

经常遇到后台线程定期更新缓存,而service线程却要实时查询缓存的功能。通常更新缓存的线程为一个,而读取缓存的线程有多个。

这正好符合读者写者模型,实际中可以用读写锁来处理。c++中也是推荐这个用法的。

实际操作中,有需要注意的细节。比如缓存不大而生成又耗时的情况下,可以先生成缓存对象,此时不需要加锁,然后在加锁的情况下去更新指针。这个指针可以是智能指针也可以是非智能指针,无论哪种情况都需要加锁。

对于智能指针情况,智能指针的拷贝、赋值以及析构等操作是线程安全的,这是因为拷贝和赋值只改变了引用计数,而析构根据引用计数可以确保是最后一个持有线程,因而也是线程安全的。reset操作需要修改指向对象的指针,因而不是线程安全。

如果是智能指针时,读取缓存的线程,只需要获zai得一个智能指针的拷贝。而如果是非智能指针模式,则需要拷贝整个对象。因为如果仍然只获得一个指针,那么谁将销毁这个指针指向对象的内容是个很大问题。

如果由写线程在更新时候销毁,那么此时可能别的读进程正持有这个指针,这个线程可能访问到delete后的内存区域,这造成了未定义行为。如果由读取线程来释放,这更不可能,因为读取线程不唯一。故如果对象很大,为了避免拷贝开销,

那么引用计数几乎是必然的选择。

至于java中,jdk5以后另有另外一种巧妙的办法:

采用一个volatile的引用保存缓存对象,这个缓存对象是不可变的,所有的字段都是final的,这保证了多线程下的构造安全性。由于java不需要考虑对象删除问题,所以有新数据时候,写线程构造一个新对象,然后直接更新这个volatile引用即可。

优点是不需要加锁操作。这也是java一个优势。这个模式一般都是应用单写多读,因为volatile不保证原子性。但是对象引用是32位,而且只牵涉到写,并不存在读改写,所以应用多写也是满足原子性的。

 

 

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页