考虑一下
public Object doGet() {
return getResource();
}
private Object getResource() {
synchronized (lock) {
if (cachedResourceIsStale()) {
downloadNewVersionOfResource();
}
}
return resource;
}
假设doGet将同时执行,并且执行大量操作,并且下载新版本的资源需要花费一段时间,那么是否有更有效的方法在getResource中进行同步?我知道读/写锁,但是我认为它们不能在这里应用.
为什么要完全同步?如果缓存过时,所有仍在被第一个资源刷新的访问资源的线程将执行自己的刷新.除其他原因外,这几乎没有效率.
正如BalusC在评论中提到的那样,我目前在servlet中遇到此问题,但是我对通用答案感到满意,因为谁知道在什么情况下我会再次遇到它.
解决方法:
假设条件
>有效意味着doGet()应该尽快完成
> cachedPageIsStale()根本不需要时间
> downloadNewVersionOfResource()需要一点时间
回答
同步减少了网络负载,因为只有一个线程会在资源到期时提取资源.此外,它不会过分延迟其他线程的处理-由于VM不包含线程可能返回的当前快照,它们将不得不阻塞,并且没有理由再执行其他并发的downloadNewVersionOfResource()会更快地完成(我想由于网络带宽争用而预期相反).
因此,同步良好,并且带宽消耗和响应时间最佳. (与I / O等待相比,同步的CPU开销几乎消失了)-假设在调用doGet()时资源的当前版本可能不可用;如果您的服务器始终具有该资源的当前版本,则可以立即将其发送回去. (您可能有一个后台线程在旧版本到期之前下载新版本.)
聚苯乙烯
您尚未显示任何错误处理.您必须决定是将downloadNewVersionOfResource()引发的异常传播到调用方,还是继续提供该资源的旧版本.
编辑
所以?假设您有100个连接工人,并且检查资源是否陈旧需要1微秒,资源不是陈旧,并且服务它需要1秒.然后,平均而言,有100 * 10 ^ -6 / 1 = 0.0001个线程试图获取该锁.几乎没有任何争论.并且获得未获取的锁的开销大约为10 ^ -8秒.当网络将导致毫秒级延迟时,没有必要优化已经占用微发送的设备.如果您不相信我,请进行微基准测试以进行同步.确实,频繁的,不必要的同步会增加大量开销,并且出于这个原因,不建议使用同步收集类.但这是因为这些方法每次调用所做的工作很少,并且同步的相对开销要大得多.我只是为以下代码做了一个小型的微基准测试:
synchronized (lock) {
c++;
}
在我的笔记本上,在sun的热点vm中,平均超过1000万次执行需要50纳秒(5 * 10 ^ -8秒).这大约是裸增量操作的20倍,因此,如果一个增量很大,将每个增量同步会使程序减慢一个数量级.但是,如果该方法确实阻塞了I / O,等待了1毫秒,则添加相同的50纳秒将使吞吐量降低0.005%.当然,您有更好的机会进行性能调整:-)
这就是为什么您应该在开始优化之前始终进行测量.它可以防止您花费大量时间来节省几纳秒的处理器时间.