108 votes

Est-ce que Java Regex est Thread Safe?

J'ai une fonction qui utilise Pattern#compile et un Matcher pour rechercher un motif dans une liste de chaînes de caractères.

Cette fonction est utilisée dans plusieurs threads. Chaque thread aura un motif unique passé à Pattern#compile lors de sa création. Le nombre de threads et de motifs est dynamique, ce qui signifie que je peux ajouter plus de Pattern et de threads pendant la configuration.

Est-ce que je dois mettre un synchronize sur cette fonction s'il utilise des regex? Regex en java est-il thread-safe?

139voto

Vineet Reynolds Points 40529

Oui, à partir de la documentation de l'API Java pour la classe Pattern

Les instances de cette classe (Pattern) sont immuables et peuvent être utilisées en toute sécurité par plusieurs threads concurrents. Les instances de la classe Matcher ne sont pas sûres pour une telle utilisation.

Si vous cherchez du code centré sur les performances, essayez de réinitialiser l'instance Matcher en utilisant la méthode reset(), au lieu de créer de nouvelles instances. Cela réinitialiserait l'état de l'instance Matcher, la rendant utilisable pour la prochaine opération regex. En fait, c'est l'état maintenu dans l'instance Matcher qui est responsable de son insécurité pour un accès concurrent.

18 votes

Les objets Pattern sont thread-safe, mais la méthode compile() pourrait ne pas l'être. Il y a eu deux ou trois bugs au fil des ans qui ont provoqué des échecs de compilation dans des environnements multithreadés. Je recommanderais de faire la compilation dans un bloc synchronisé.

4 votes

Oui, il y a eu des bugs de concurrence soulevés dans la classe Pattern, et votre conseil d'accès synchronisé est apprécié. Cependant, les développeurs d'origine de la classe Pattern ont eu l'intention de rendre la classe Pattern sûre pour les threads, et c'est le contrat sur lequel tout programmeur Java devrait pouvoir compter. Pour être honnête, je préférerais avoir des variables locales par thread et accepter la légère perte de performance plutôt que de compter sur un comportement sûr pour les threads par contrat (à moins que je n'aie vu le code). Comme on dit "La gestion des threads est facile, la synchronisation correcte est difficile".

1 votes

Notez que la source de "Pattern" se trouve dans la distribution d'Oracle JDK (Selon oracle.com/technetwork/java/faq-141681.html#A14 : "Le Java 2 SDK, Standard Edition contient lui-même un fichier appelé src.zip qui contient le code source des classes publiques du package java") donc on peut jeter un rapide coup d'œil soi-même.

13voto

AVD Points 57984

Sécurité des threads avec les expressions régulières en Java

RÉSUMÉ :

L'API d'expressions régulières Java a été conçue pour permettre à un seul motif compilé d'être partagé entre plusieurs opérations de correspondance.

Vous pouvez appeler en toute sécurité Pattern.matcher() sur le même motif à partir de threads différents et utiliser les matchers en toute sécurité de manière concurrente. Pattern.matcher() est sûr pour construire des matchers sans synchronisation. Bien que la méthode ne soit pas synchronisée, à l'intérieur de la classe Pattern, une variable volatile appelée compilée est toujours définie après la construction d'un motif et lue au début de l'appel à matcher(). Cela oblige tout thread faisant référence au Pattern à "voir" correctement le contenu de cet objet.

En revanche, vous ne devriez pas partager un Matcher entre différents threads. Ou tout au moins, si vous le faisiez un jour, vous devriez utiliser une synchronisation explicite.

2 votes

@akf, Au fait, vous devriez noter que c'est un site de discussion (tout comme celui-ci). Je considérerais toute information que vous y trouvez ni meilleure ni pire que celle que vous trouveriez ici (c'est-à-dire, ce n'est pas La Parole Vraie de James Gosling).

3voto

Bob Cross Points 13552

Alors, en bref, si vous faites quelque chose comme l'exemple :

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

vous devriez être plutôt bien.

Suivi de l'exemple de code pour plus de clarté : notez que cet exemple implique fortement que le Matcher ainsi créé est thread-local avec le Pattern et le test. Autrement dit, vous ne devez pas exposer le Matcher ainsi créé à d'autres threads.

Honnêtement, c'est le risque de n'importe quelle question de sécurité des threads. La réalité est que n'importe quel code peut être rendu non sûr pour les threads si vous vous en donnez la peine. Heureusement, il existe des merveilleux livres qui nous enseignent une multitude de façons dont nous pourrions ruiner notre code. Si nous évitons ces erreurs, nous réduisons grandement notre propre probabilité de problèmes de threading.

0 votes

@Jason S : la localité du fil est un moyen très direct d'atteindre la sécurité des fils même si le code interne n'est pas thread-safe. Si seulement une méthode pourrait jamais accéder à une méthode particulière à la fois, vous avez imposé la sécurité des fils de manière externe.

1 votes

Ok, donc vous dites simplement que recréer un modèle à partir d'une chaîne au point d'utilisation est préférable à le stocker pour être efficace, au risque de traiter avec les problèmes de concurrence ? Je vous l'accorde. J'étais confus avec cette phrase sur les méthodes de fabrique et les constructeurs publics, ça semble être une piste de diversion par rapport à ce sujet.

0 votes

@Jason S, non, les méthodes de fabrique et le manque de constructeurs sont quelques-unes des façons dont vous pouvez réduire la menace de couplage avec d'autres threads. Si la seule façon d'obtenir le Matcher qui va avec mon Pattern est via p.matcher(), personne d'autre ne peut modifier mon Matcher de manière indésirable. Cependant, je peux quand même causer des problèmes à moi-même : si j'ai une méthode publique qui retourne ce Matcher, un autre thread pourrait y accéder et le modifier de manière indésirable. En bref, la concurrence est difficile (dans N'IMPORTE quel langage).

2voto

akf Points 23518

Un rapide coup d'œil au code de Matcher.java montre un tas de variables membres incluant le texte qui est en cours d'appariement, des tableaux pour les groupes, quelques index pour maintenir la localisation et quelques boolean pour d'autres états. Tout cela pointe vers un Matcher étatique qui ne se comporterait pas bien s'il était accédé par plusieurs Threads. C'est ce que dit aussi la JavaDoc:

Les instances de cette classe ne sont pas sécurisées pour une utilisation par plusieurs threads concurrents.

Ce n'est un problème que si, comme le souligne @Bob Cross, vous faites en sorte de permettre l'utilisation de votre Matcher dans des Threads séparés. Si vous avez besoin de le faire, et que vous pensez que la synchronisation posera problème pour votre code, une option que vous avez est d'utiliser un objet de stockage ThreadLocal pour maintenir un Matcher par thread de travail.

1voto

George Birbilis Points 43

En résumé, vous pouvez réutiliser (conserver dans des variables statiques) les motifs Pattern compilés et leur demander de vous fournir de nouveaux Matchers lorsque nécessaire pour valider ces motifs regex contre une chaîne de caractères

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

/**
 * Aide à la validation
 */
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);
  }

  /**
   * Vérifie si l'e-mail est valide
   */
  public static boolean isValidEmail(String email) { 
    Matcher matcher = email_pattern.matcher(email);
    return matcher.matches();
  }

}

voir http://zoomicon.wordpress.com/2012/06/01/validating-e-mails-using-regular-expressions-in-java/ (près de la fin) concernant le motif RegEx utilisé ci-dessus pour valider les e-mails (au cas où il ne conviendrait pas aux besoins de validation des e-mails tels qu'ils sont présentés ici)

3 votes

Merci d'avoir posté votre réponse! Merci de lire attentivement la FAQ sur l'auto-promotion. Quelqu'un pourrait voir cette réponse et l'article de blog lié et penser que vous avez posté l'article de blog simplement pour pouvoir y faire un lien d'ici.

2 votes

Pourquoi s'embêter avec static {}? Vous pouvez intégrer cette initialisation de variable et rendre le Pattern final également.

1 votes

Je suis d'accord avec l'opinion de TWiStErRob : private static final Pattern emailPattern = Pattern.compile(EMAIL_PATTERN); est mieux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X