线程安全的Java池,具有即时读取功能
要编写一个功能齐全的Java对象池,使用读/写锁不是什么大问题。 我看到的问题是,读取操作将不得不等到存储监视器(或类似的东西,取决于型号)被释放,这确实会减慢它的速度 因此,应满足以下要求:线程安全的Java池,具有即时读取功能,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,要编写一个功能齐全的Java对象池,使用读/写锁不是什么大问题。 我看到的问题是,读取操作将不得不等到存储监视器(或类似的东西,取决于型号)被释放,这确实会减慢它的速度 因此,应满足以下要求: 读取(或获取)操作应该是即时的——使用某个键,应该立即返回对象的最新版本,而无需等待任何锁定 写入(创建/更新)-可能已排队,时间延迟合理,可能正在等待某个存储锁 有代码示例吗 我没有找到直接针对该问题的问题 它在一些讨论中突然出现,但我找不到一个问题完全涉及在Java中创建这样一个池的问题 当对数据结
我没有找到直接针对该问题的问题
它在一些讨论中突然出现,但我找不到一个问题完全涉及在Java中创建这样一个池的问题 当对数据结构的修改花费太长时间时(无论出于何种原因),简单地等待并写入锁定结构将不会成功。您无法预见何时有足够的时间在不阻止任何读取的情况下执行修改 您唯一能做的(尝试做的)就是将写操作的时间减少到最小。正如@assylias所述,CopyOnWrite*通过在写操作时克隆数据结构来实现这一点,并在操作完成时自动激活修改后的结构 这样,读取锁定将花费克隆操作的持续时间加上切换引用的时间。您可以将其扩展到数据结构的一小部分:如果只更改对象中的状态,则可以修改该对象的副本,然后将更复杂的数据结构中的引用更改为该副本 另一种方法是在读取操作时或之前进行复制。通常,无论如何,您都会通过数据结构的API返回对象的副本,因此只需“缓存”该副本,在修改过程中,读卡器就可以访问缓存的副本。这就是数据库缓存aso所做的 这取决于你的型号,什么最适合你。如果您对易于复制的数据进行的写操作很少,那么CopyOnWrite的性能可能会最好。如果要进行大量的写入,最好提供结构的单个“读取”/“缓存”状态,并不时切换
AtomicReference<Some> datastructure = ...;
//copy on write
synchronized /*one writer*/ void change(Object modification)
throws CloneNotSupportedException {
Object copy = datastructure.clone();
apply(copy, modification);
datastructure.set(copy);
}
Object search(Object select) {
return datastructure.get().search(select);
}
// copy for read
AtomicReference<Some> cached = new AtomicReference<Some>(datastructure.get().clone());
synchronized void change(Object modification) {
apply(datastructure, modification);
cached.set(datastructure);
}
Object search(Object select) {
return cached.get().search(select);
}
原子参考数据结构=。。。;
//抄写
已同步/*一个写入程序*/void更改(对象修改)
抛出CloneNotSupportedException{
Object copy=datastructure.clone();
申请(复印、修改);
数据结构集(副本);
}
对象搜索(对象选择){
返回datastructure.get().search(选择);
}
//抄读
AtomicReference cached=新的AtomicReference(datastructure.get().clone());
同步的无效更改(对象修改){
应用(数据结构、修改);
cached.set(数据结构);
}
对象搜索(对象选择){
返回cached.get().search(选择);
}
对于这两种操作,读取时没有等待。。但此时它需要切换引用。在这种情况下,您可以简单地使用volatile变量,以避免在读卡器端锁定,并使用
synchronized
方法保持写操作的独占性<代码>易失性会给读取增加很少甚至没有开销,但写入速度会有点慢。根据预期的吞吐量和读/写比率,这可能是一个很好的解决方案
class Cache {
private volatile Map<K, V> cache; //Assuming map is the right data structure
public V get(K key) {
return cache.get(key);
}
//synchronized writes for exclusive access
public synchronized void put(K key, V value) {
Map<K, V> copy = new HashMap<> (cache);
V value = copy.put(key, value);
//volatile guarantees that this will be visible from the getter
cache = copy;
return value;
}
}
类缓存{
私有易失性映射缓存;//假设映射是正确的数据结构
公共V get(K键){
返回cache.get(key);
}
//用于独占访问的同步写入
公共同步作废put(K键,V值){
映射副本=新哈希映射(缓存);
V值=复制.放置(键,值);
//volatile保证这将从getter中可见
缓存=复制;
返回值;
}
}
这是一个完全无锁的Java对象池解决方案。供参考
使用什么?类似于CopyOnWriteArrayList的模式可能会有所帮助。写操作获取写锁,复制池,将新项添加到副本,最后将池引用更改为副本,以便读者可以访问新池。通过使池不稳定,您可能可以完全解除读取端的锁定。这正是我所认为的CopyOnWriteArrayList,唯一的问题是-引用重新分配是线程安全的操作吗?在使用
原子引用时是这样的。