Java Regex İş Parçacığı Güvenli mi?


104

Bir desen için dizelerin listesini aramak için Pattern#compileve a kullanan Matcherbir işlevim var.

Bu işlev, birden çok iş parçacığında kullanılır. Her iş parçacığı Pattern#compile, iş parçacığı oluşturulduğunda geçirilen benzersiz bir desene sahip olacaktır . İş parçacığı ve desen sayısı dinamiktir, yani Patternyapılandırma sırasında daha fazla s ve iş parçacığı ekleyebilirim .

Bir koymak gerekiyor mu synchronizeo regex kullanıyorsa bu fonksiyonu üzerine? Java iş parçacığındaki normal ifade güvenli midir?

Yanıtlar:


132

Evet , Pattern sınıfı için Java API belgelerinden

Bu (Desen) sınıfının örnekleri değişmezdir ve birden çok eşzamanlı iş parçacığı tarafından kullanım için güvenlidir. Matcher sınıfının örnekleri bu tür bir kullanım için güvenli değildir.

Performans merkezli koda bakıyorsanız, yeni örnekler oluşturmak yerine reset () yöntemini kullanarak Matcher örneğini sıfırlamayı deneyin. Bu, Matcher örneğinin durumunu sıfırlayarak onu bir sonraki normal ifade işlemi için kullanılabilir hale getirir. Aslında, eşzamanlı erişim için güvensiz olmasından sorumlu olan, Matcher örneğinde tutulan durumdur.


17
Desen nesneleri iş parçacığı açısından güvenlidir, ancak compile()yöntem olmayabilir. Yıllar içinde, çok iş parçacıklı ortamlarda derlemenin başarısız olmasına neden olan iki veya üç hata olmuştur. Derlemeyi senkronize bir blokta yapmanızı tavsiye ederim.
Alan Moore

4
Evet, Pattern sınıfında eşzamanlılık hataları ortaya çıktı ve senkronize erişim tavsiyeniz takdire şayan. Bununla birlikte, Pattern sınıfının orijinal geliştiricileri, Pattern sınıfını iş parçacığı güvenli hale getirmeyi amaçladı ve bu, herhangi bir Java programcısının güvenebileceği sözleşmedir. Açıkçası, iş parçacığı yerel değişkenlere sahip olmayı ve sözleşmeye göre iş parçacığı güvenli davranışına güvenmektense minimum performans isabetini kabul etmeyi tercih ederim (kodu görmedim). Dedikleri gibi "Diş çekme kolay, doğru senkronizasyon zordur".
Vineet Reynolds

1
"Kalıp" kaynağının Oracle JDK dağıtımında olduğuna dikkat edin ( oracle.com/technetwork/java/faq-141681.html#A14'e göre : "Java 2 SDK, Standard Edition'ın kendisi src.zip adlı bir dosya içerir. java paketindeki genel sınıfların kaynak kodunu içerir "), böylece kişi hızlı bir şekilde göz atabilir.
David TONHOFER

@DavidTonhofer En son JDK'mızın hatasız doğru koda sahip olabileceğini düşünüyorum, ancak Java'nın ara .class dosyaları herhangi bir uyumlu VM tarafından herhangi bir platformda yorumlanabildiğinden, bu düzeltmelerin bu çalışma zamanında var olduğundan emin olamazsınız. Elbette çoğu zaman sunucunun hangi sürümü çalıştırdığını bilirsiniz, ancak her bir sürümü kontrol etmek sıkıcıdır.
TWiStErRob

12

Java'da normal ifadelerle iş parçacığı güvenliği

ÖZET:

Java normal ifade API'si, tek bir derlenmiş modelin birden çok eşleştirme işleminde paylaşılmasına izin verecek şekilde tasarlanmıştır.

Aynı desen üzerinde Pattern.matcher () 'ı farklı iş parçacıklarından güvenle çağırabilir ve eşleştiricileri aynı anda güvenle kullanabilirsiniz. Pattern.matcher () , senkronizasyon olmadan eşleştiriciler oluşturmak için güvenlidir. Yöntem, Pattern sınıfına dahil olmak üzere senkronize edilmese de, derlenmiş adı verilen uçucu bir değişken her zaman bir desen oluşturduktan sonra ayarlanır ve matcher () çağrısının başlangıcında okunur . Bu, Desene atıfta bulunan herhangi bir iş parçacığını o nesnenin içeriğini doğru bir şekilde "görmeye" zorlar.

Öte yandan, bir Eşleştiriciyi farklı iş parçacıkları arasında paylaşmamalısınız. Ya da en azından, yaptıysanız, açık senkronizasyon kullanmalısınız.


2
@akf, BTW, bunun bir tartışma sitesi olduğuna dikkat etmelisiniz (buna çok benziyor). Orada bulduğunuz herhangi bir şeyi burada bulacağınız bilgiden daha iyi veya daha kötü olarak düşünmüyorum (yani, James Gosling'den Tek Doğru Söz değil).
Bob Cross

3

İş parçacığı güvenliğinin çevreleyen kodu da hesaba katması gerektiğini hatırlamanız gerekse de, şanslı görünüyorsunuz. Aslında Eşleyiciler Desen en kullanılarak oluşturulur eşleştirici fabrika yöntemini ve kamu kurucular eksikliği olumlu bir işarettir. Aynı şekilde, çevreleyen Kalıbı oluşturmak için derleme statik yöntemini kullanırsınız .

Kısacası, örnek gibi bir şey yaparsanız:

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

oldukça iyi yapmalısın.

Açıklık için kod örneğini takip edin: Bu örneğin, bu şekilde oluşturulan Eşleştiricinin Desen ve test ile iş parçacığı yerel olduğunu kuvvetle ima ettiğini unutmayın. Yani, bu şekilde oluşturulan Eşleştiriciyi başka herhangi bir konuya maruz bırakmamalısınız.

Açıkçası, bu herhangi bir iş parçacığı güvenliği sorusunun riski. Gerçek şu ki , yeterince sıkı çalışırsanız herhangi bir kod iş parçacığı açısından güvenli hale getirilebilir. Neyse ki, bize kodumuzu mahvedebileceğimiz pek çok yol öğreten harika kitaplar var. Bu hatalardan uzak durursak, kendi problemleri çözme olasılığımızı büyük ölçüde azaltırız.


@Jason S: iş parçacığı yerelliği, dahili kod iş parçacığı güvenli olmasa bile iş parçacığı güvenliğini sağlamanın çok basit bir yoludur. Aynı anda yalnızca bir yöntem belirli bir yönteme erişebiliyorsa, iş parçacığı güvenliğini harici olarak uyguladınız.
Bob Cross

1
Tamam, yani sadece kullanım noktasında bir dizgiden bir kalıbı yeniden oluşturmanın, eşzamanlılık sorunları ile başa çıkma riski altında verimli olması için saklamaktan daha iyi olduğunu mu söylüyorsunuz? sana bunu vereceğim. Fabrika yöntemleri ve kamu kurucuları hakkındaki bu cümleyle kafam karıştı, bu konuyla birlikte kırmızı ringa balığı gibi görünüyor.
Jason S

@Jason S, hayır, fabrika yöntemleri ve kurucu eksikliği, diğer ipliklerle birleşme tehdidini azaltmanın yollarından bazıları. Kalıbımla birlikte gelen Eşleştiriciyi elde etmenin tek yolu p.matcher () yoluyla ise, başka hiç kimse Eşleştiricime yan etki yapamaz. Bununla birlikte, yine de kendim için sorun yaratabilirim: eğer o Matcher'ı döndüren halka açık bir yöntemim varsa, başka bir iş parçacığı ona ulaşabilir ve onu yan etkileyebilir. Kısacası, eşzamanlılık zordur (HERHANGİ bir dilde).
Bob Cross

2

Koduna hızlı bir bakış, Matcher.javaeşleştirilen metin, gruplar için diziler, konumu korumak için birkaç dizin ve booleandiğer durum için birkaç s dahil olmak üzere bir grup üye değişkenini gösterir . Bunların tümü Matcher, birden fazla erişim sağlandığında iyi davranmayacak bir durum bilgisine işaret eder Threads. Yani yapar javadoc :

Bu sınıfın örnekleri, birden çok eşzamanlı iş parçacığı tarafından kullanım için güvenli değildir.

Bu yalnızca, @ Bob Cross'un da belirttiği gibi, kendi e-postalarınızı Matcherayrı Threade- postalarda kullanmanıza izin vermek için yolunuzdan çekilirseniz bir sorundur . Bunu yapmanız gerekiyorsa ve senkronizasyonun kodunuz için bir sorun olacağını düşünüyorsanız, sahip olduğunuz bir seçenek, her çalışan iş parçacığını ThreadLocalkorumak için bir depolama nesnesi kullanmaktır Matcher.


1

Özetlemek gerekirse, derlenen Kalıpları yeniden kullanabilir (statik değişkenlerde tutabilir) ve bu normal ifade kalıplarını bir dizeye göre doğrulamak için gerektiğinde size yeni Eşleştiriciler vermelerini söyleyebilirsiniz.

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

}

E-postaları doğrulamak için yukarıda kullanılan RegEx kalıbı ile ilgili olarak http://zoomicon.wordpress.com/2012/06/01/validating-e-mails-using-regular-expressions-in-java/ (sonuna yakın) bakın ( burada yayınlandığı gibi e-posta doğrulama gereksinimlerine uymuyorsa)


3
Cevabınızı gönderdiğiniz için teşekkürler! Lütfen Kendini Tanıtma ile ilgili SSS'yi dikkatlice okuduğunuzdan emin olun . Birisi bu yanıtı ve bağlantılı blog gönderisini görebilir ve blog gönderisini yalnızca buradan bağlantı verebilmek için yayınladığınızı düşünebilir.
Andrew Barber

2
Neden uğraşıyorsun static {}? Bu değişken ilklendirmeyi satır içi yapabilir ve Pattern final.
TWiStErRob

1
TWiStErRob'un görüşünü ikinci olarak görüyorum: private static final Pattern emailPattern = Pattern.compile(EMAIL_PATTERN);daha iyi.
Christophe Roussy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.