activity - android java singleton example
¿Es seguro el hilo de DocumentBuilder? (3)
La especificación JAXP (V 1.4) dice:
Se espera que el nuevo método SAXParser de una implementación de SAXParserFactory, el método newDocumentBuilder de un DocumentBuilderFactory y el método newTransformer de un TransformerFactory sean seguros para hilos sin efectos secundarios. Esto significa que un programador de aplicaciones debe poder crear instancias de transformadores en múltiples hilos a la vez desde una fábrica compartida sin efectos secundarios o problemas.
https://jaxp.java.net/docs/spec/html/#plugabililty-thread-safety
Entonces, por ejemplo, debería poder crear una sola instancia de DocumentBuilderFactory a través de DocumentBuilderFactory.newInstance y luego usar esa fábrica única para crear un DocumentBuilder por subproceso a través de DocumentBuilderFactory.newDocumentBuilder. También podría crear un grupo de DocumentBuilders.
No puedo encontrar ningún lugar que diga que, por ejemplo, el método estático DocumentBuilderFactory.newInstance es seguro para subprocesos. La implementación parece segura para subprocesos en el sentido de que se está realizando alguna sincronización de métodos, pero la especificación dice específicamente que DocumentBuilderFactory.newDocumentBuilder es seguro para subprocesos.
La base de código actual que estoy viendo usa el analizador DOM. El siguiente fragmento de código se duplica en 5 métodos:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Si un método que contiene el código anterior se llama en un bucle o se llama al método varias veces en la aplicación, estamos cargando la sobrecarga de crear una nueva instancia de DocumentBuilderFactory y una nueva instancia de DocumentBuilder para cada llamada a dicho método.
¿Sería una buena idea crear un contenedor de singleton alrededor de la fábrica de DocumentBuilder y las instancias de DocumentBuilder como se muestra a continuación?
public final class DOMParser {
private DocumentBuilderFactory = new DocumentBuilderFactory();
private DocumentBuilder builder;
private static DOMParser instance = new DOMParser();
private DOMParser() {
builder = factory.newDocumentBuilder();
}
public Document parse(InputSource xml) {
return builder.parser(xml);
}
}
¿Hay algún problema que pueda surgir si el singleton anterior se comparte entre varios hilos? De lo contrario, ¿habrá algún aumento en el rendimiento al usar el enfoque anterior de crear las instancias de DocumentBuilderFactory y DocumentBuilder solo una vez durante la vida útil de la aplicación?
Editar:
El único momento en que podemos enfrentar un problema es si DocumentBuilder guarda cierta información de estado al analizar un archivo XML que puede afectar el análisis del próximo archivo XML.
Necesitas saber tres cosas:
- ¿Cuál es el costo de crear la fábrica? Si el costo es bajo, su ganancia de rendimiento puede ser cercana a cero.
- ¿Cuál es el costo de crear el constructor? Si el costo es bajo, su ganancia de rendimiento puede ser cercana a cero.
- ¿Es seguro el hilo de fábrica y / o del constructor? De lo contrario, debes asegurarte de que el método de acceso a ellos esté protegido contra subprocesos con la palabra clave
synchronized
.
No estoy familiarizado con las clases de DocumentBuilder que está utilizando, pero toda esta información debe estar disponible en su javadoc u otra documentación. Si la creación de ciertos objetos es costosa, por lo general le arrojan esta información.
Vea la sección de comentarios para otras preguntas sobre el mismo asunto. Respuesta breve a su pregunta: no, no está bien colocar estas clases en un singleton. Ni DocumentBuilderFactory ni DocumentBuilder garantizan la seguridad de los subprocesos. Si tiene varios hilos que analizan XML, asegúrese de que cada hilo tenga su propia versión de DoumentBuilder. Solo necesita uno de ellos por hilo, ya que puede reutilizar un DocumentBuilder después de restablecerlo.
EDITAR Un pequeño fragmento para mostrar que usar el mismo DocumentBuilder es malo. Con java 1.6_u32 y 1.7_u05 este código falla con org.xml.sax.SAXException: FWK005 parse may not be called while parsing
. Descomenta la sincronización en el generador, y funciona bien:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder builder = factory.newDocumentBuilder();
ExecutorService exec = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
exec.submit(new Runnable() {
public void run() {
try {
// synchronized (builder) {
InputSource is = new InputSource(new StringReader("<?xml version=/"1.0/" encoding=/"UTF-8/" ?><俄语>данные</俄语>"));
builder.parse(is);
builder.reset();
// }
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
exec.shutdown();
Así que aquí está su respuesta: no llame a DocumentBuilder.parse()
desde múltiples hilos. Sí, este comportamiento puede ser específico de JRE, si está utilizando IBM java o JRockit o le da un DocumentBuilderImpl diferente, podría funcionar bien, pero para la implementación de xerces predeterminada, no es así.