Apakah Java Regex Thread Aman?

104

Saya memiliki fungsi yang menggunakan Pattern#compiledan Matcheruntuk mencari daftar string untuk suatu pola.

Fungsi ini digunakan di banyak utas. Setiap utas akan memiliki pola unik yang diteruskan ke Pattern#compilesaat utas dibuat. Jumlah utas dan pola dinamis, artinya saya dapat menambahkan lebih banyak Patterns dan utas selama konfigurasi.

Apakah saya perlu synchronizemenggunakan fungsi ini jika menggunakan regex? Apakah regex di thread java aman?

jmq
sumber

Jawaban:

132

Ya , dari dokumentasi Java API untuk kelas Pattern

Instance dari kelas (Pola) ini tidak dapat diubah dan aman untuk digunakan oleh beberapa utas bersamaan. Instance dari kelas Matcher tidak aman untuk penggunaan seperti itu.

Jika Anda melihat kode yang berpusat pada kinerja, coba setel ulang instance Matcher menggunakan metode reset (), alih-alih membuat instance baru. Ini akan mengatur ulang status instance Matcher, membuatnya dapat digunakan untuk operasi regex berikutnya. Faktanya, itu adalah negara yang dipertahankan dalam contoh Matcher yang bertanggung jawab untuk menjadi tidak aman untuk akses bersamaan.

Vineet Reynolds
sumber
17
Objek pola aman untuk benang, tetapi compile()metodenya mungkin tidak. Ada dua atau tiga bug selama bertahun-tahun yang menyebabkan kompilasi gagal di lingkungan multithread. Saya akan merekomendasikan melakukan kompilasi dalam blok tersinkronisasi.
Alan Moore
4
Ya, ada bug konkurensi yang muncul di kelas Pola, dan saran Anda tentang akses tersinkronisasi sangat dihargai. Namun, pengembang asli dari kelas Pattern bermaksud untuk menjadikan kelas Pattern sebagai thread yang aman, dan itu adalah kontrak yang harus dapat diandalkan oleh setiap programmer Java. Sejujurnya, saya lebih suka memiliki variabel lokal utas dan menerima kinerja minimal yang dicapai daripada mengandalkan perilaku aman utas berdasarkan kontrak (kecuali saya telah melihat kodenya). Seperti yang mereka katakan "Mengulir itu mudah, sinkronisasi yang benar itu sulit".
Vineet Reynolds
1
Perhatikan bahwa sumber "Pola" ada di distribusi Oracle JDK (Menurut oracle.com/technetwork/java/faq-141681.html#A14 : "Java 2 SDK, Edisi Standar itu sendiri berisi file bernama src.zip yang berisi kode sumber untuk kelas-kelas publik dalam paket java ") sehingga seseorang dapat mengintip sendiri.
David Tonhofer
@DavidTonhofer Saya pikir JDK terbaru kami mungkin memiliki kode bebas bug yang benar, tetapi karena file .class perantara Java dapat diinterpretasikan pada platform apa pun oleh VM yang kompatibel, Anda tidak dapat memastikan perbaikan tersebut ada di runtime itu. Tentu saja sebagian besar waktu Anda tahu versi mana yang dijalankan server, tetapi membosankan untuk memeriksa setiap versi.
TWiStErRob
12

Keamanan thread dengan ekspresi reguler di Java

RINGKASAN:

API ekspresi reguler Java telah dirancang untuk memungkinkan satu pola terkompilasi untuk dibagikan ke beberapa operasi pencocokan.

Anda bisa dengan aman memanggil Pattern.matcher () pada pola yang sama dari utas berbeda dan dengan aman menggunakan matcher secara bersamaan. Pattern.matcher () aman untuk membuat matcher tanpa sinkronisasi. Meskipun metode ini tidak disinkronkan, internal ke kelas Pola, variabel volatil yang disebut dikompilasi selalu disetel setelah membuat pola dan dibaca di awal panggilan ke matcher (). Ini memaksa utas apa pun yang merujuk ke Pola untuk "melihat" konten objek itu dengan benar.

Di sisi lain, Anda tidak boleh membagikan Matcher di antara utas yang berbeda. Atau setidaknya, jika pernah melakukannya, Anda harus menggunakan sinkronisasi eksplisit.

adatapost
sumber
2
@akf, BTW, Anda harus mencatat bahwa itu adalah situs diskusi (seperti yang ini). Saya akan menganggap apa pun yang Anda temukan di sana tidak lebih baik atau lebih buruk daripada informasi yang Anda temukan di sini (yaitu, itu bukan The One True Word From James Gosling).
Bob Cross
3

Meskipun Anda perlu mengingat bahwa keamanan utas juga harus mempertimbangkan kode di sekitarnya, Anda tampaknya beruntung. Fakta bahwa Pencocokan dibuat menggunakan metode pabrik pencocokkan Pola dan kurangnya konstruktor publik adalah tanda positif. Demikian juga, Anda menggunakan metode statis kompilasi untuk membuat Pola yang mencakup .

Jadi, singkatnya, jika Anda melakukan sesuatu seperti contoh:

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

kamu harus melakukannya dengan cukup baik.

Tindak lanjut dari contoh kode untuk kejelasan: perhatikan bahwa contoh ini sangat menyiratkan bahwa Matcher yang dibuat adalah thread-local dengan Pola dan pengujian. Yaitu, Anda tidak boleh mengekspos Matcher yang dibuat ke utas lainnya.

Terus terang, itulah risiko dari pertanyaan keamanan utas apa pun. Kenyataannya adalah bahwa kode apa pun dapat dibuat thread-unsafe jika Anda berusaha cukup keras. Untungnya, ada buku bagus yang mengajari kita banyak cara untuk merusak kode kita. Jika kita menjauh dari kesalahan itu, kita sangat mengurangi kemungkinan masalah threading kita sendiri.

Bob Cross
sumber
@Jason S: lokalitas thread adalah salah satu cara yang sangat mudah untuk mencapai keamanan thread meskipun kode internal tidak aman untuk thread. Jika hanya satu metode yang mungkin dapat mengakses metode tertentu dalam satu waktu, Anda telah menerapkan keamanan thread secara eksternal.
Bob Cross
1
ok, jadi Anda hanya mengatakan bahwa membuat ulang pola dari string pada titik penggunaan, lebih baik daripada menyimpannya agar efisien, dengan risiko menangani masalah konkurensi? aku akan memberimu itu. Saya bingung dengan kalimat tentang metode pabrik dan konstruktor publik, yang tampaknya seperti topik bahasan merah.
Jason S
@Jason S, tidak, metode pabrik dan kurangnya konstruktor adalah beberapa cara yang dapat Anda lakukan untuk mengurangi ancaman penggandengan dengan utas lain. Jika satu-satunya cara Anda bisa mendapatkan Matcher yang sesuai dengan Pola saya adalah melalui p.matcher (), tidak ada orang lain yang bisa memberikan efek samping Matcher saya. Namun, saya masih dapat menimbulkan masalah bagi diri saya sendiri: jika saya memiliki metode publik yang mengembalikan Matcher itu, utas lain dapat melakukannya dan memberikan efek samping. Singkatnya, konkurensi itu sulit (dalam bahasa APA SAJA).
Bob Cross
2

Sekilas kode untuk Matcher.javamenunjukkan sekelompok variabel anggota termasuk teks yang cocok, array untuk grup, beberapa indeks untuk memelihara lokasi dan beberapa booleanuntuk negara lain. Ini semua mengarah ke stateful Matcheryang tidak akan berperilaku baik jika diakses oleh banyak orang Threads. Begitu juga dengan JavaDoc :

Instance dari kelas ini tidak aman untuk digunakan oleh beberapa thread bersamaan.

Ini hanya masalah jika, seperti yang dikatakan @Bob Cross, Anda berusaha keras untuk mengizinkan penggunaan Anda Matcherdi Threads yang terpisah . Jika Anda perlu melakukan ini, dan menurut Anda sinkronisasi akan menjadi masalah bagi kode Anda, opsi yang Anda miliki adalah menggunakan ThreadLocalobjek penyimpanan untuk mempertahankan Matcherper thread yang berfungsi.

akf
sumber
1

Singkatnya, Anda dapat menggunakan kembali (tetap dalam variabel statis) Pola yang telah dikompilasi dan memberi tahu mereka untuk memberi Anda Matcher baru ketika diperlukan untuk memvalidasi pola regex tersebut terhadap beberapa string

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

}

lihat http://zoomicon.wordpress.com/2012/06/01/validating-e-mails-using-regular-expressions-in-java/ (di bagian akhir) mengenai pola RegEx yang digunakan di atas untuk memvalidasi email ( jika tidak sesuai dengan kebutuhan untuk validasi email seperti yang diposting di sini)

George Birbilis
sumber
3
Terima kasih telah memposting jawaban Anda! Harap pastikan untuk membaca FAQ tentang Promosi Mandiri dengan cermat. Seseorang mungkin melihat jawaban ini dan postingan blog yang ditautkan ke dan mengira Anda memposting postingan blog itu hanya agar Anda dapat menautkannya dari sini.
Andrew Barber
2
Mengapa repot-repot static {}? Anda dapat memasukkan inisialisasi variabel itu dan membuat Pattern finaljuga.
TWiStErRob
1
Saya setuju dengan pendapat TWiStErRob: private static final Pattern emailPattern = Pattern.compile(EMAIL_PATTERN);lebih baik.
Christophe Roussy