[Web Service] Rop在进行响应或输入的序列化时存在线程安全问题
stamen
2012-05-17
由于我原来的Marshaller是一个RopResponse对应一个实例,也即一个RopResponse类是共享一个单实例的Marshaller。当时,我认为Marshaller是线程安全的,但是经过测试,Marshaller是非线程安全的:
可以看这篇文章: http://jaxb.java.net/guide/Performance_and_thread_safety.html 我马上会修复这个BUG的。 |
|
stamen
2012-05-18
RopResponseMarshaller的实现类包括以下两个:
org.codehaus.jackson.map.ObjectMapper是线程安全的(参见:http://wiki.fasterxml.com/JacksonFAQThreadSafety),而javax.xml.bind.Marshaller是非线程安全的。原来的Rop对ObjectMapper和Marshaller都采用了单实例的方式,因此存在线程安全的问题。 关于javax.xml.bind.Marshaller非线程安全的说明参见:http://jaxb.java.net/guide/Performance_and_thread_safety.html 不但javax.xml.bind.Marshaller是非线程安全的,Spring OXM的Marshaller也是非线程安全的,参见其javadoc的说明: 引用 createMarshaller protected Marshaller createMarshaller() Return a newly created JAXB marshaller. JAXB marshallers are not necessarily thread safe. 原来的JaxbXmlRopResponseMarshaller: public class JaxbXmlRopResponseMarshaller implements RopResponseMarshaller { private static Map<Class, Marshaller> marshallerMap = new HashMap<Class, Marshaller>(); public void marshaller(RopResponse response, OutputStream outputStream) { try { Marshaller m = getMarshaller(response); m.marshal(response, outputStream); } catch (JAXBException e) { throw new RopException(e); } } private Marshaller getMarshaller(RopResponse response) throws JAXBException { if (!marshallerMap.containsKey(response.getClass())) { JAXBContext context = JAXBContext.newInstance(response.getClass()); Marshaller marshaller = context.createMarshaller();//Marshaller是非线程安全的,不能多线程共用 !!!! marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(Marshaller.); marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8"); marshallerMap.put(response.getClass(), marshaller); } return marshallerMap.get(response.getClass()); } } 更改为: public class JaxbXmlRopResponseMarshaller implements RopResponseMarshaller { private static Map<Class, JAXBContext> jaxbContextHashMap = new ConcurrentHashMap<Class, JAXBContext>(); public void marshaller(RopResponse response, OutputStream outputStream) { try { Marshaller m = buildMarshaller(response); m.marshal(response, outputStream); } catch (JAXBException e) { throw new RopException(e); } } public Marshaller buildMarshaller(RopResponse response) throws JAXBException { if (!jaxbContextHashMap.containsKey(response.getClass())) { JAXBContext context = JAXBContext.newInstance(response.getClass());//JAXBContext是线程安全的 jaxbContextHashMap.put(response.getClass(), context); } JAXBContext context = jaxbContextHashMap.get(response.getClass()); Marshaller marshaller = context.createMarshaller();//每次创建一个新的Marshaller marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8"); return marshaller; } } 其实,根据测试创建Marshaller的时间很短,只有1毫秒左右,而真正慢的是创建JAXBContext,大家100毫秒左右。所以,这样改后,对Rop序列化的性能不会有什么影响,因为JAXBContext是共享的。 已经同步到git中。 |