在Java中同步String对象我有一个webapp,我正在进行一些负载/性能测试,特别是在我们希望有几百个用户访问同一页面并在此页面上每10秒点击一次刷新的功能。我们发现我们可以使用此功能进行改进的一个方面是在一段时间内缓存来自Web服务的响应,因为数据没有变化。在实现这个基本缓存之后,在一些进一步的测试中,我发现我没有考虑并发线程如何同时访问Cache。我发现在大约100毫秒内,大约有50个线程试图从Cache中获取对象,发现它已经过期,命中Web服务以获取数据,然后将对象放回缓存中。原始代码看起来像这样:private SomeData[] getSomeDataByEmail(WebServiceInterface service, String email) {
final String key = "Data-" + email;
SomeData[] data = (SomeData[]) StaticCache.get(key);
if (data == null) {
data = service.getSomeDataForEmail(email);
StaticCache.set(key, data, CACHE_TIME);
}
else {
logger.debug("getSomeDataForEmail: using cached object");
}
return data;}因此,为了确保在对象key过期时只有一个线程正在调用Web服务,我认为我需要同步Cache get / set操作,并且似乎使用缓存键是一个很好的候选对象同步(这样,对电子邮件b@b.com的此方法的调用不会被方法调用a@a.com阻止)。我将方法更新为如下所示:private SomeData[] getSomeDataByEmail(WebServiceInterface service, String email) {
SomeData[] data = null;
final String key = "Data-" + email;
synchronized(key) {
data =(SomeData[]) StaticCache.get(key);
if (data == null) {
data = service.getSomeDataForEmail(email);
StaticCache.set(key, data, CACHE_TIME);
}
else {
logger.debug("getSomeDataForEmail: using cached object");
}
}
return data;}我希望在get / set操作周围一次只能看到一个线程进入/退出同步块。在String对象上同步是否存在问题?我认为缓存键是一个很好的选择,因为它对于操作是唯一的,即使final String key在方法中声明,我也认为每个线程都会获得对同一个对象的引用,因此会同步单个对象。我在这做错了什么?
3 回答
慕尼黑5688855
TA贡献1848条经验 获得超2个赞
在intern'd String上进行同步可能根本不是一个好主意 - 通过实习,String变成一个全局对象,如果你在应用程序的不同部分同步interned字符串,你可能会变得非常奇怪,基本上是不可解决的同步问题,例如死锁。这似乎不太可能,但是当它发生时,你真的被搞砸了。作为一般规则,只能在本地对象上进行同步,您绝对确定模块外部的代码不会锁定它。
在您的情况下,您可以使用同步哈希表来存储密钥的锁定对象。
例如:
Object data = StaticCache.get(key, ...);if (data == null) { Object lock = lockTable.get(key); if (lock == null) { // we're the only one looking for this lock = new Object(); synchronized(lock) { lockTable.put(key, lock); // get stuff lockTable.remove(key); } } else { synchronized(lock) { // just to wait for the updater } data = StaticCache.get(key); }} else { // use from cache}
此代码具有竞争条件,其中两个线程可能会将对象彼此放入锁定表中。然而,这应该不是问题,因为那时你只有一个线程调用webservice并更新缓存,这应该不是问题。
如果您在一段时间后使缓存失效,则应在从锁定!= null情况下从缓存中检索数据后再检查数据是否为空。
或者,更容易,您可以使整个缓存查找方法(“getSomeDataByEmail”)同步。这意味着所有线程在访问缓存时都必须进行同步,这可能是性能问题。但是一如既往,首先尝试这个简单的解决方案,看看它是否真的是一个问题!在许多情况下它不应该是,因为您可能花费更多时间处理结果而不是同步。
添加回答
举报
0/150
提交
取消