Java Regex 스레드는 안전합니까?


104

내가 사용하는 기능을 가지고 Pattern#compile와이 Matcher패턴에 대한 문자열 목록을 검색 할 수 있습니다.

이 함수는 여러 스레드에서 사용됩니다. 각 스레드에는 고유 한 패턴이Pattern#compile 에는 스레드가 생성 될 때 . 스레드와 패턴의 수는 동적이므로 Pattern구성 중에 더 많은 스레드와 스레드를 추가 할 수 있습니다 .

synchronize정규식을 사용하는 경우이 함수에 추가해야 합니까? Java 스레드의 정규식은 안전합니까?

답변:


132

, Pattern 클래스에 대한 Java API 문서에서

이 (Pattern) 클래스의 인스턴스는 변경할 수 없으며 여러 동시 스레드에서 사용하기에 안전합니다. Matcher 클래스의 인스턴스는 이러한 사용에 안전하지 않습니다.

성능 중심 코드를보고있는 경우 새 인스턴스를 만드는 대신 reset () 메서드를 사용하여 Matcher 인스턴스를 재설정하십시오. 이렇게하면 Matcher 인스턴스의 상태가 재설정되어 다음 정규식 작업에 사용할 수 있습니다. 실제로 동시 액세스에 대해 안전하지 않은 것은 Matcher 인스턴스에서 유지되는 상태입니다.


17
패턴 객체는 스레드로부터 안전하지만 compile()메서드는 그렇지 않을 수 있습니다. 멀티 스레드 환경에서 컴파일이 실패하는 원인이 된 버그가 수년 동안 2 ~ 3 개있었습니다. 동기화 된 블록에서 컴파일을 수행하는 것이 좋습니다.
Alan Moore

4
예, Pattern 클래스에서 동시성 버그가 발생했으며 동기화 된 액세스에 대한 귀하의 조언에 감사드립니다. 그러나 Pattern 클래스의 원래 개발자는 Pattern 클래스를 스레드로부터 안전하게 만들려고했으며 이는 모든 Java 프로그래머가 신뢰할 수있는 계약입니다. 솔직히 말해서, 스레드 로컬 변수를 사용하고 계약에 의한 스레드 안전 동작에 의존하는 것보다 최소한의 성능 적중을 허용하는 것이 좋습니다 (코드를 보지 않은 경우). "스레딩은 쉽고 올바른 동기화는 어렵다"고 말합니다.
Vineet Reynolds

1
"Pattern"의 소스는 Oracle JDK 배포에 있습니다 ( oracle.com/technetwork/java/faq-141681.html#A14 에 따르면 : "Java 2 SDK, Standard Edition 자체에는 src.zip이라는 파일이 포함되어 있습니다. 자바 패키지에있는 공용 클래스에 대한 소스 코드가 포함되어 있습니다. ") 따라서 빠르게 살펴볼 수 있습니다.
David Tonhofer 2013

@DavidTonhofer 최신 JDK에 버그가없는 올바른 코드가있을 수 있다고 생각하지만 Java의 중간 .class 파일은 호환 가능한 VM에 의해 모든 플랫폼에서 해석 될 수 있기 때문에 해당 수정 사항이 해당 런타임에 존재하는지 확신 할 수 없습니다. 물론 대부분의 경우 서버가 실행중인 버전을 알고 있지만 모든 단일 버전을 확인하는 것은 지루합니다.
TWiStErRob

12

Java에서 정규식을 사용한 스레드 안전성

요약:

Java 정규식 API는 여러 일치 작업에서 단일 컴파일 된 패턴을 공유 할 수 있도록 설계되었습니다.

다른 스레드에서 동일한 패턴에 대해 Pattern.matcher () 를 안전하게 호출 하고 동시에 매처를 안전하게 사용할 수 있습니다. Pattern.matcher () 는 동기화없이 매처를 생성하는 것이 안전합니다. 메서드가 동기화되지는 않지만 Pattern 클래스 내부에 있지만, 패턴을 생성 한 후에는 항상 compile이라는 휘발성 변수가 설정되고 matcher () 호출이 시작될 때 읽습니다 . 이렇게하면 Pattern을 참조하는 모든 스레드가 해당 개체의 내용을 올바르게 "볼"수 있습니다.

반면에 서로 다른 스레드간에 Matcher를 공유해서는 안됩니다. 또는 적어도 그렇게했다면 명시 적 동기화를 사용해야합니다.


2
@akf, BTW,이 사이트가 토론 사이트라는 점에 유의해야합니다 (이 사이트와 매우 유사 함). 여기서 찾을 수있는 정보보다 더 좋거나 나쁘다고 생각되는 것은 무엇이든 고려할 것입니다 (즉, James Gosling의 The One True Word가 아닙니다).
Bob Cross

3

스레드 안전성은 주변 코드도 고려해야한다는 것을 기억해야하지만 운이 좋은 것 같습니다. 사실 매처 (Matchers)이 패턴의 사용하여 만들어집니다 정규의 팩토리 메소드와 public 생성자 부족은 긍정적이다. 마찬가지로 컴파일 정적 메서드를 사용하여 포함하는 패턴 을 만듭니다. .

간단히 말해 예와 같은 작업을 수행하면 다음과 같습니다.

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

당신은 꽤 잘하고 있어야합니다.

명확성을 위해 코드 예제에 대한 후속 조치 :이 예제는 이렇게 생성 된 Matcher가 Pattern 및 테스트와 함께 스레드 로컬임을 강력하게 암시합니다. 즉, 이렇게 생성 된 Matcher를 다른 스레드에 노출해서는 안됩니다.

솔직히 이것은 스레드 안전성 질문의 위험입니다. 현실은 충분히 노력하면 모든 코드가 스레드에 안전하지 않게 될 수 있다는 것입니다. 다행히도 우리가 코드를 망칠 수있는 모든 방법을 가르쳐주는 멋진 들이 있습니다. 이러한 실수를 피하면 스레딩 문제가 발생할 가능성이 크게 줄어 듭니다.


@Jason S : 스레드 지역 성은 내부 코드가 스레드로부터 안전하지 않더라도 스레드 안전성을 달성하는 매우 간단한 방법 중 하나입니다. 한 번에 하나의 메서드 만 특정 메서드에 액세스 할 수 있다면 외부 적으로 스레드 안전을 적용한 것입니다.
Bob Cross

1
좋아, 그래서 당신은 사용 시점에서 문자열에서 패턴을 다시 만드는 것이 동시성 문제를 다루는 위험을 감수하면서 효율적으로 저장하는 것보다 낫다는 것을 말하고 있습니까? 내가 허락 할게. 나는이 주제가없는 붉은 청어처럼 보이는 팩토리 메서드와 공개 생성자에 관한 그 문장과 혼동되었다.
Jason S

@Jason S, 아니요, 팩토리 메서드와 생성자 부족은 다른 스레드와의 결합 위협을 줄일 수있는 몇 가지 방법입니다. 내 패턴에 맞는 Matcher를 얻을 수있는 유일한 방법이 p.matcher ()를 통하는 것이라면 다른 누구도 내 Matcher를 부작용으로 만들 수 없습니다. 그러나 여전히 문제를 일으킬 수 있습니다. 해당 Matcher를 반환하는 공용 메서드가있는 경우 다른 스레드가이를 가져와 부작용을 일으킬 수 있습니다. 요컨대, 동시성은 어렵습니다 (모든 언어에서).
Bob Cross

2

에 대한 코드를 간략히 살펴보면 Matcher.java일치하는 텍스트, 그룹 배열, 위치 유지 관리를위한 몇 가지 인덱스, boolean다른 상태를위한 몇 가지를 포함하는 여러 멤버 변수가 표시 됩니다. 이 모든 Matcher것은 다중에서 액세스 할 경우 제대로 작동하지 않는 상태 저장 을 가리 킵니다 Threads. JavaDoc도 마찬가지입니다 .

이 클래스의 인스턴스는 여러 동시 스레드에서 사용하기에 안전하지 않습니다.

@Bob Cross가 지적했듯이 Matcher별도 Thread의 s 에서 사용을 허용하는 경우에만 문제가됩니다 . 이 작업을 수행해야하고 코드에서 동기화가 문제가 될 것이라고 생각하는 경우에는 ThreadLocal저장소 개체를 사용하여 Matcher작업 스레드 당 유지 관리 할 수 있습니다.


1

요약하면, 컴파일 된 패턴을 재사용 (정적 변수에 유지)하고 일부 문자열에 대해 해당 정규식 패턴의 유효성을 검사해야 할 때 새 매처를 제공하도록 지시 할 수 있습니다.

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

}

이메일 유효성 검사에 사용 된 RegEx 패턴에 대해서는 http://zoomicon.wordpress.com/2012/06/01/validating-e-mails-using-regular-expressions-in-java/ (끝 근처)를 참조하십시오. 여기에 게시 된대로 이메일 검증을위한 요구 사항에 맞지 않는 경우)


3
답변을 게시 해 주셔서 감사합니다! Self-Promotion에 대한 FAQ를 주의 깊게 읽어 보시기 바랍니다 . 누군가가이 답변과 링크 된 블로그 게시물을보고 여기에서 링크 할 수 있도록 블로그 게시물을 게시했다고 생각할 수 있습니다.
Andrew Barber

2
왜 귀찮게 static {}? 그 변수 초기화를 인라인하고 만들 수도 Pattern final있습니다.
TWiStErRob

1
나는 두 번째로 TWiStErRob : private static final Pattern emailPattern = Pattern.compile(EMAIL_PATTERN);더 낫다.
Christophe Roussy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.