为了保障原创作者在本站发表文章的利益, 并维护本站原创的精神, 特声明: RIAShanghai对有以下任何情况之一的文章将不通知作者并直接进行快意删除:
- 非原创, 或者原创但一文多发;
- 各种形式的广告与吹擂;
- 不符合本站文章格式.
欢迎各位读者监督. 谢谢合作. 另: 作为Adobe正式的UG, 我们将把Adobe不定期分发的软件,书籍及各种纪念品赠送给发文活跃的作者, 共同进步.
I use Actionscript on the client side, and it's extremely easy as there is only one thread running (ignoring the system related threads like networking, etc). However, on the sever side, things are getting pretty complicated. Consider the following code:
public class ServiceCenter {
private static PicklistsCache picklistCache;
public static PicklistsCache getPicklistCache() {
if(picklistCache == null) {
picklistCache = new PicklistsCache(this);
}
return picklistCache;
}There are multiple requests from the client side and multiple threads will call the getPicklistCache method. How many number of PicklistsCache will be created? Most people will answer one - which is incorrect. There could one one, two, or many more instances of PicklistsCache will be created. Why? Let's say there are 2 threads execute at both line 06, at this time the class variable picklistCache is null, so each thread will go ahead and create a PicklistsCache. Thus, two instances of PicklistsCache are created. In the end, the value of picklistCache will be overwritten by the slower thread.
To fix it, you simply use a synchronized block. Note, you need to re-check whether the variable is null - if 2 threads execute at line 7 below, one thread will be arrange to pass the synchronized block and the other will be queue to wait for the completion of the first. Of course, you can simply add synchronized to the method declaration, but doing so will hurt the performance.
public class ServiceCenter {
private static PicklistsCache picklistCache;
public PicklistsCache getPicklistCache() {
if(picklistCache == null) {
synchronized(ServiceCenter.class) {
if(picklistCache == null) {
picklistCache = new PicklistsCache(this);
}
}
}
return picklistCache;
}
}This kind of concurrency problems occur frequently on the sever side. You need to monitor closely to detect and fix them before getting bitten.
UPDATE: Oct 06, 2008 - concurrency bug fixed
// Must use volatile to avoid concurrency problem. private volatile ToMany _relAttributesOwnedCached; /** Fast look up map for attributes: cdName -> Attribute */ private volatile Map_attributesMap; private void refreshAttributesMap() { if(_attributesMap == null) { _attributesMap = new ConcurrentHashMap (64); } _attributesMap.clear(); List attrs = getAttributesOwned(); for(int i = 0; attrs != null && i < attrs.size(); i++) { Attribute attr = (Attribute) attrs.get(i); _attributesMap.put(attr.getSystemName(), attr); } } /** * Finds the attribute with the given SQL column name (case-sensitive) - fast look up with Map. * @param cdName the SQL column name for the attribute. * @return the attribute with the given SQL column name or nullif not found. */ public Attribute getAttributeBySystemName(String systemName) { ToMany attrsOwned = (ToMany) getRelationshipObjectOrCreateResolve(REL_attributesOwned, false); if(attrsOwned.isCommitted()) { if(_relAttributesOwnedCached != attrsOwned) { synchronized (this) { if(_relAttributesOwnedCached != attrsOwned) { refreshAttributesMap(); _relAttributesOwnedCached = attrsOwned; } } } if(_attributesMap.get(systemName) == null) { log.warn("getAttributeBySystemName(" + systemName + ") not found: - " + toString(true)); } return _attributesMap.get(systemName); }else{ ... } }
Two tips: