Ofbiz2.1有两个bug,都涉及到线程安全性,小并发的时候不容易发现,大并发下有时候会出现,并发数越高出现的频度就比较高,尤其对于实体引擎的那个bug,在系统初始化的时候如果遭遇大并发,会有一定频度的出现。
1。entity engine的ModelEntity.getField方法存在线程安全隐患,会造成 XXXX is not a field of XXX的异常,以下是原有代码片断:
public ModelField getField(String fieldName) {
if (fieldName == null) return null;
if (fieldsMap == null) {
fieldsMap = new HashMap(fields.size());
for (int i = 0; i < fields.size(); i++) {
ModelField field = (ModelField) fields.get(i);
fieldsMap.put(field.name, field);
}
return (ModelField) fieldsMap.get(fieldName);
}
由于getField方法没有同步(会造成性能下降),因此红色标标注的那段代码存在线程安全问题,必须进行同步。在大并发下如果多个调用这个方法,最先调用的线程没有执行完循环的情况下,后续的线程通过最后的语句return的时候得到的就是Null(fieldsMap已经被第一个线程赋值了,后续线程不会进入红色标准的代码区域)。
修改后的代码如下:
public ModelField getField(String fieldName) {
if (fieldName == null) return null;
if (fieldsMap == null) {
createFields();
}
return (ModelField) fieldsMap.get(fieldName);
}
public synchronized void createFields()
{
fieldsMap = new HashMap(fields.size());
for (int i = 0; i < fields.size(); i++) {
ModelField field = (ModelField) fields.get(i);
fieldsMap.put(field.name, field);
}
}
这个Bug在3.0中已经被修正。
2。UtilCache.get方法同样存在线程安全隐患,会造成LinkedList.remove或者LinedList.addFirst的空值针异常,不注意还会以为是LinkedList的bug。以下是原代码片断:
public Object get(Object key) {
if (key == null) {
missCount++;
return null;
}
UtilCache.CacheLine line = (UtilCache.CacheLine) cacheLineTable.get(key);
if (hasExpired(line)) {
// note that print.info in debug.properties cannot be checked through UtilProperties here, it would cause infinite recursion
// if (Debug.infoOn()) Debug.logInfo("Element has expired with key " + key);
remove(key);
line = null;
}
if (line == null) {
// if (Debug.infoOn()) Debug.logInfo("Element not found with key " + key);
missCount++;
return null;
}
// if (Debug.infoOn()) Debug.logInfo("Element found with key " + key);
hitCount++;
if (maxSize > 0) {
keyLRUList.remove(key);
keyLRUList.addFirst(key);
}
return line.getValue();
}
红色标准的部分是有问题的代码,修改后的代码如下:
public Object get(Object key) {
if (key == null) {
missCount++;
return null;
}
UtilCache.CacheLine line = (UtilCache.CacheLine) cacheLineTable.get(key);
if (hasExpired(line)) {
// note that print.info in debug.properties cannot be checked through UtilProperties here, it would cause infinite recursion
// if (Debug.infoOn()) Debug.logInfo("Element has expired with key " + key);
remove(key);
line = null;
}
if (line == null) {
// if (Debug.infoOn()) Debug.logInfo("Element not found with key " + key);
missCount++;
return null;
}
// if (Debug.infoOn()) Debug.logInfo("Element found with key " + key);
hitCount++;
if (maxSize > 0) {
synchronized ( this)
{
keyLRUList.remove(key);
keyLRUList.addFirst(key);
}
}
return line.getValue();
}
这个BUG在3.0种也修正了。