我很好奇这个缓存的想法是否有效:
@RequiredArgsConstructor @Getter class CacheEntry {
static CacheEntry get(String string, int start) {
int hash = string.hashCode() ^ start; // or something better
int index = hash & (cache.length-1);
CacheEntry result = cache[index];
if (result!=null && result.matches(string, start)) return result;
result = new CacheEntry(string, start, computeSomething(string, start));
cache[index] = result;
return result;
}
private boolean matches(String string, int start) {
if (string.equals(this.string)) return false;
if (start == this.start) return false;
return true;
}
private static ImmutableSomething computeSomething(String string, int start) {
...
}
private static final CacheEntry[] cache = new CacheEntry[256];
private final String string;
private final int start;
private final ImmutableSomething something;
}
注释来自lombok。试想一下每个人的名字都是如此。
目标是将呼叫保存到computeSomething
并最小化分配。
缓存既不是同步的,也不是本地的线程。我们无法保证一个线程会看到另一个线程完成的更新。这是可以接受的。也没有任何保证,一个线程不会覆盖另一个线程的条目。这也是可以接受的。
在我写的一个小基准测试中,与sane caching alternatives相比,它带来了很好的加速。我关心的是正确性:可能会发生一个线程看到无效条目(例如,一个包含错误something
的条目)?
答案 0 :(得分:3)
只要CacheEntry
是一个正确的不可变对象(而不仅仅是一个有效的不可变对象),这就可以工作。这是因为可以安全地发布不可变对象而不进行同步,并且对象引用赋值是原子的。
换句话说,如果CacheEntry
不是完全不可变的,那么它是不安全的,因为消费者线程可能会看到一个不完全构造的对象。此外,如果缓存的是原始类型,其分配不是原子的(double
,long
),那么使用者线程可能会看到垃圾(半分配的值)。
修改强>:
根据{{3}},如果符合以下条件,则可以安全地发布对象而不进行同步:
final
this
关键字在构建期间不会转义)