Czy wątek Java Regex jest bezpieczny?


104

Mam funkcję, która używa Pattern#compilei Matcherdo przeszukiwania listy ciągów dla wzorca.

Ta funkcja jest używana w wielu wątkach. Każdy wątek będzie miał unikalny wzór przekazany do, Pattern#compilegdy zostanie utworzony. Liczba wątków i wzorców jest dynamiczna, co oznacza, że ​​mogę dodać więcej Patterns i wątków podczas konfiguracji.

Czy muszę umieszczać synchronizena tej funkcji, jeśli używa ona wyrażenia regularnego? Czy wyrażenie regularne w wątku Java jest bezpieczne?

Odpowiedzi:


132

Tak , z dokumentacji Java API dla klasy Pattern

Wystąpienia tej klasy (Pattern) są niezmienne i można je bezpiecznie używać przez wiele współbieżnych wątków. Instancje klasy Matcher nie są bezpieczne do takiego użycia.

Jeśli patrzysz na kod skoncentrowany na wydajności, spróbuj zresetować instancję Matcher za pomocą metody reset (), zamiast tworzyć nowe instancje. Spowoduje to zresetowanie stanu instancji Matcher, umożliwiając jej użycie w następnej operacji regex. W rzeczywistości to stan utrzymywany w instancji Matcher jest odpowiedzialny za to, że jest ona niebezpieczna dla równoczesnego dostępu.


17
Obiekty Pattern są bezpieczne wątkowo, ale compile()metoda może nie być. Na przestrzeni lat pojawiły się dwa lub trzy błędy, które powodowały niepowodzenie kompilacji w środowiskach wielowątkowych. Poleciłbym kompilację w zsynchronizowanym bloku.
Alan Moore

4
Tak, w klasie Pattern zostały zgłoszone błędy współbieżności, a Twoja rada dotycząca zsynchronizowanego dostępu jest mile widziana. Jednak pierwotni programiści klasy Pattern zamierzali uczynić klasę Pattern bezpieczną dla wątków i jest to kontrakt, na którym powinien polegać każdy programista Java. Szczerze mówiąc, wolałbym wątkować zmienne lokalne i akceptować minimalną wydajność niż polegać na zachowaniu bezpiecznym wątków na podstawie kontraktu (chyba że widziałem kod). Jak mówią „Wątkowanie jest łatwe, poprawna synchronizacja jest trudna”.
Vineet Reynolds

1
Zwróć uwagę, że źródło „Pattern” znajduje się w dystrybucji Oracle JDK (według oracle.com/technetwork/java/faq-141681.html#A14 : „Zestaw Java 2 SDK, Standard Edition zawiera plik o nazwie src.zip, zawiera kod źródłowy dla klas publicznych w pakiecie java ”), więc można sobie szybko zerknąć.
David Tonhofer

@DavidTonhofer Myślę, że nasz najnowszy JDK może mieć poprawny, wolny od błędów kod, ale ponieważ pośrednie pliki .class Javy mogą być interpretowane na dowolnej platformie przez dowolną kompatybilną maszynę wirtualną, nie możesz być pewien, że te poprawki istnieją w tym środowisku wykonawczym. Oczywiście przez większość czasu wiesz, która wersja działa na serwerze, ale sprawdzanie każdej wersji jest żmudne.
TWiStErRob

12

Bezpieczeństwo wątków dzięki wyrażeniom regularnym w Javie

PODSUMOWANIE:

Interfejs API wyrażeń regularnych języka Java został zaprojektowany tak, aby umożliwić współużytkowanie pojedynczego skompilowanego wzorca w wielu operacjach dopasowywania.

Możesz bezpiecznie wywołać Pattern.matcher () na tym samym wzorcu z różnych wątków i bezpiecznie używać dopasowań jednocześnie. Pattern.matcher () umożliwia bezpieczne tworzenie dopasowań bez synchronizacji. Chociaż metoda nie jest zsynchronizowana, wewnątrz klasy Pattern, zmienna zmienna o nazwie compiled jest zawsze ustawiana po skonstruowaniu wzorca i odczytywana na początku wywołania funkcji matcher (). Wymusza to na każdym wątku odwołującym się do Patternu poprawne „widzenie” zawartości tego obiektu.

Z drugiej strony nie powinieneś udostępniać Matchera między różnymi wątkami. A przynajmniej, jeśli kiedykolwiek to zrobiłeś, powinieneś użyć jawnej synchronizacji.


2
@akf, BTW, powinieneś zauważyć, że jest to strona dyskusyjna (podobna do tej). Uważam, że wszystko, co tam znajdziesz, nie jest lepsze ani gorsze niż informacje, które tu znajdziesz (tj. Nie jest to Jedyne prawdziwe słowo Jamesa Goslinga).
Bob Cross

3

Chociaż musisz pamiętać, że bezpieczeństwo wątków musi również uwzględniać otaczający kod, wydaje się, że masz szczęście. Fakt, że dopasowujące są tworzone przy użyciu metody fabryki wzorców matcherów i brakuje publicznych konstruktorów, jest pozytywnym znakiem. Podobnie, używasz statycznej metody kompilacji , aby utworzyć obejmujący Pattern .

Krótko mówiąc, jeśli zrobisz coś takiego jak przykład:

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

powinieneś sobie dobrze radzić.

Kontynuuj przykładowy kod dla jasności: zwróć uwagę, że ten przykład silnie sugeruje, że utworzony w ten sposób element dopasowujący jest lokalny wątku ze wzorcem i testem. To znaczy, nie powinieneś ujawniać utworzonego w ten sposób Matchera żadnym innym wątkom.

Szczerze mówiąc, istnieje ryzyko jakichkolwiek pytań dotyczących bezpieczeństwa nici. W rzeczywistości każdy kod może stać się niebezpieczny dla wątków, jeśli wystarczająco się postarasz. Na szczęście istnieją wspaniałe książki, które uczą nas wielu sposobów zrujnowania naszego kodu. Jeśli trzymamy się z daleka od tych błędów, znacznie zmniejszamy własne prawdopodobieństwo problemów z gwintowaniem.


@Jason S: lokalizacja wątku to bardzo prosty sposób na osiągnięcie bezpieczeństwa wątku, nawet jeśli kod wewnętrzny nie jest bezpieczny dla wątków. Jeśli tylko jedna metoda mogłaby kiedykolwiek uzyskać dostęp do określonej metody w danym momencie, wymuszono zewnętrzne zabezpieczenie wątków.
Bob Cross

1
ok, więc po prostu mówisz, że ponowne utworzenie wzorca z ciągu w miejscu jego użycia jest lepsze niż przechowywanie go, aby był wydajny, z ryzykiem rozwiązania problemów ze współbieżnością? przyznam ci to. Zmyliło mnie to zdanie o metodach fabrycznych i publicznych konstruktorach, które wydaje się być czerwonym śledziem w / r / t tego tematu.
Jason S

@Jason S, nie, fabryczne metody i brak konstruktorów to tylko niektóre ze sposobów na zmniejszenie zagrożenia sprzężeniem z innymi wątkami. Jeśli jedynym sposobem na uzyskanie dopasowania dopasowanego do mojego wzorca jest p.matcher (), nikt inny nie może wywołać efektu ubocznego mojego Matchera. Jednak nadal mogę sprawiać sobie kłopoty: jeśli mam publiczną metodę, która zwraca ten Matcher, inny wątek mógłby się do niej dostać i wywołać efekt uboczny. Krótko mówiąc, współbieżność jest trudna (w DOWOLNYM języku).
Bob Cross

2

Szybkie spojrzenie na kod dla Matcher.javapokazuje kilka zmiennych składowych, w tym tekst, który jest dopasowywany, tablice dla grup, kilka indeksów dla utrzymania lokalizacji i kilka booleandla innego stanu. To wszystko wskazuje na stan Matcher, który nie zachowywałby się dobrze, gdyby był używany przez wiele osób Threads. Tak robi JavaDoc :

Wystąpienia tej klasy nie są bezpieczne do użycia przez wiele współbieżnych wątków.

Jest to problem tylko wtedy, gdy, jak wskazuje @Bob Cross, zrobisz wszystko, co w twojej mocy, aby zezwolić na korzystanie z Matcheroddzielnych plików Thread. Jeśli musisz to zrobić i myślisz, że synchronizacja będzie problemem dla twojego kodu, możesz użyć ThreadLocalobiektu pamięci do obsługi Matcherwątku roboczego.


1

Podsumowując, możesz ponownie wykorzystać (zachować w zmiennych statycznych) skompilowany wzorzec (wzorce) i powiedzieć im, aby w razie potrzeby dali Ci nowe dopasowania, aby sprawdzić poprawność tych wzorów wyrażeń regularnych względem jakiegoś ciągu

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();
  }

}

zobacz http://zoomicon.wordpress.com/2012/06/01/validating-e-mails-using-regular-expressions-in-java/ (pod koniec) w sprawie wzorca RegEx używanego powyżej do sprawdzania poprawności wiadomości e-mail ( w przypadku, gdy nie odpowiada potrzebom weryfikacji wiadomości e-mail, jak jest to zamieszczone tutaj)


3
Dziękujemy za przesłanie odpowiedzi! Przeczytaj uważnie często zadawane pytania dotyczące autopromocji . Ktoś może zobaczyć tę odpowiedź i link do wpisu na blogu i pomyśleć, że opublikowałeś post na blogu tylko po to, abyś mógł utworzyć łącze do niego z tego miejsca.
Andrew Barber

2
Po co zawracać sobie głowę static {}? Możesz wstawić tę zmienną inicjalizacyjną i zrobić Pattern finalrównież.
TWiStErRob,

1
Po drugie, TWiStErRob: private static final Pattern emailPattern = Pattern.compile(EMAIL_PATTERN);jest lepszy.
Christophe Roussy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.