2009-09-01 16 views
86

Tengo una función que usa Pattern.compile y un Matcher para buscar una lista de cadenas para un patrón. Esta función se usa en múltiples hilos. Cada hilo tendrá un patrón único que se pasará a Pattern.compile cuando se cree el hilo. La cantidad de hilos y patrones son dinámicos, lo que significa que puedo agregar más patrones e hilos durante la configuración.¿Es seguro Java Regex Thread?

¿Necesito poner una "sincronización" en esta función si usa expresiones regulares? ¿Es segura la expresión regular en el hilo de Java?

TIA

Respuesta

108

, de la documentación de la API Java para los Pattern class

instancias de esta clase (Patrón) son inmutables y son seguros para el uso de múltiples subprocesos simultáneos. Las instancias de la clase Matcher no son seguras para tal uso.

Si está buscando un código centrado en el rendimiento, intente restablecer la instancia de Matcher utilizando el método reset(), en lugar de crear instancias nuevas. Esto restablecería el estado de la instancia de Matcher, haciéndolo utilizable para la próxima operación de expresión regular. De hecho, es el estado mantenido en la instancia de Matcher el responsable de que no sea seguro para el acceso simultáneo.

+15

Los objetos de patrón son seguros para subprocesos, pero el método 'compile()' podría no serlo.Hubo dos o tres errores a lo largo de los años que provocaron la falla de la compilación en entornos multiproceso. Yo recomendaría hacer la compilación en un bloque sincronizado. –

+2

Sí, se han producido errores de concurrencia en la clase Patrón, y se agradecen sus consejos de acceso sincronizado. Sin embargo, los desarrolladores originales de la clase Pattern pretendían hacer que la clase Pattern fuera segura para el hilo, y ese es el contrato en el que cualquier programador Java debería poder confiar. Para ser sincero, preferiría tener variables locales de hilos y aceptar el golpe de rendimiento mínimo que confiar en el comportamiento seguro de subprocesos por contrato (a menos que haya visto el código). Como dicen "Enhebrar es fácil, la sincronización correcta es difícil". –

+1

Tenga en cuenta que la fuente de "Patrón" está en la distribución de Oracle JDK (Según http://www.oracle.com/technetwork/java/faq-141681.html#A14: "El SDK de Java 2, Standard Edition en sí mismo contiene un archivo llamado src.zip que contiene el código fuente de las clases públicas en el paquete java ") para que uno pueda echar un rápido vistazo. –

3

Si bien es necesario recordar que la seguridad hilo tiene que tomar en cuenta el código alrededores, así, que parecen estar en suerte. El hecho de que Matchers se creen usando el método de fábrica matcher de Pattern y carezca de constructores públicos es un signo positivo. Del mismo modo, utiliza el método estático compile para crear el abarcador Pattern.

Así, en pocas palabras, si haces algo como el ejemplo:

Pattern p = Pattern.compile("a*b"); 
Matcher m = p.matcher("aaaaab"); 
boolean b = m.matches(); 

que debería estar haciendo bastante bien.

Seguimiento del ejemplo del código para mayor claridad: tenga en cuenta que este ejemplo implica fuertemente que el Matcher así creado es thread-local con el Patrón y la prueba. Es decir, no debe exponer el Matcher así creado a ningún otro hilo.

Francamente, ese es el riesgo de cualquier cuestión de seguridad de hilos. La realidad es que cualquier código puede hacerse inseguro si se esfuerza lo suficiente. Afortunadamente, hay wonderfulbooks que nos enseñan un montón de formas en que podríamos arruinar nuestro código. Si nos mantenemos alejados de esos errores, reducimos en gran medida nuestra propia probabilidad de problemas.

+0

¿qué tiene esto que ver con la seguridad hilo? –

+0

@Jason S: localidad de subprocesos es una forma muy sencilla de lograr seguridad de subprocesos incluso si el código interno no es seguro para subprocesos. Si solo un método puede acceder alguna vez a un método en particular a la vez, ha aplicado la seguridad del hilo externamente. –

+1

bien, por lo que apenas está diciendo que volver a crear un patrón de una cuerda en el punto de uso, es mejor que el almacenamiento que sea eficiente, con el riesgo de hacer frente a los problemas de concurrencia? te lo concederé. Estaba confundido con esa frase sobre métodos de fábrica y constructores públicos, que parece ser una arenga sin este tema. –

8

Thread-safety with regular expressions in Java

RESUMEN:

La API de expresiones regulares Java ha sido diseñado para permitir que un solo patrón compilado para ser compartido a través de los partidos operaciones múltiples.

Puede llamar de forma segura Pattern.matcher() en el mismo patrón de diferentes hilos y utilizar con seguridad los igualadores de forma concurrente. Pattern.matcher() es seguro para construir emparejamientos sin sincronización. Aunque el método no está sincronizado, interna a la clase patrón, una variable volátil llamados compilan siempre se fija después de construir un patrón y leer al inicio de la llamada a matcher(). Esto fuerza cualquier hilo que se refiera a el Patrón para "ver" correctamente el contenido de ese objeto.

Por otro lado, no debe compartir un Matcher entre diferentes subprocesos. O al menos, si alguna vez lo hizo, debe usar sincronización explícita.

+2

@akf, por cierto, se debe tener en cuenta que eso es un sitio de debate (como éste). Considero que todo lo que encuentres allí no es mejor o peor que la información que encontrarás aquí (es decir, no es The One True Word de James Gosling). –

2

Una mirada rápida en el código para Matcher.java muestra un montón de variables miembro que incluye el texto que se está igualada, las matrices de los grupos, unos índices para mantener la ubicación y unos boolean s para otro estado. Todo esto apunta a un estado Matcher que no se comportaría bien si se accede por múltiple Threads. Lo mismo ocurre con la JavaDoc:

instancias de esta clase no son seguros para su uso por múltiples hilos concurrentes.

esto es sólo un problema si, como señala @Bob Cruz, de salir de su manera de permitir el uso de su Matcher en distintos Thread s. Si necesita hacer esto, y usted piensa que la sincronización será un problema para su código, una opción que tienes es utilizar un objeto ThreadLocal de almacenamiento para mantener un Matcher por subproceso de trabajo.

1

En resumen, se puede reutilizar (tener en variables estáticas) el Patrón (s) compilado y diles que le dará nuevos comparadores cuando sea necesario para validar los zuecos de expresiones regulares contra un trozo de cuerda

import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

/** 
* Validation helpers 
*/ 
public final class Validators { 

private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$"; 

private static Pattern email_pattern; 

    static { 
    email_pattern = Pattern.compile(EMAIL_PATTERN); 
    } 

    /** 
    * Check if e-mail is valid 
    */ 
    public static boolean isValidEmail(String email) { 
    Matcher matcher = email_pattern.matcher(email); 
    return matcher.matches(); 
    } 

} 

ver http://zoomicon.wordpress.com/2012/06/01/validating-e-mails-using-regular-expressions-in-java/ (cerca del final) en relación con el patrón de expresión se ha usado anteriormente para la validación de mensajes de correo electrónico (en caso de que no se ajusta a las necesidades de uno para la validación de correo electrónico, ya que se ha escrito aquí)

+3

¡Gracias por publicar tu respuesta! Asegúrese de leer detenidamente [Preguntas frecuentes sobre autopromoción] (http://stackoverflow.com/faq#promotion). Es posible que alguien vea esta respuesta y la publicación del blog vinculada y piense que publicó la publicación del blog simplemente para poder vincularla desde aquí. –

+1

¿Por qué molestarse con 'static {}'? Puede alinear esa inicialización variable y hacer que el 'Patrón'' final' también. – TWiStErRob

+1

En segundo lugar, la opinión de TWiStErRob: 'private static final Pattern emailPattern = Pattern.compile (EMAIL_PATTERN);' es mejor. –