Perangkap Thread-Safe dalam Pemrosesan XML – Beragampengetahuan
Apakah menurut Anda metodenya children() Berikut ini adalah keamanan utas?
import java.util.stream.Stream;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public final class SafeXml {
private final Node node;
SafeXml(final Node node) {
this.node = node.cloneNode(true);
}
public Stream<SafeXml> children() {
NodeList nodes = this.node.getChildNodes();
int length = nodes.getLength();
return Stream.iterate(0, idx -> idx + 1)
.limit(length)
.map(nodes::item)
.map(SafeXml::new);
}
}
Tentu saja, karena saya mengajukan pertanyaan ini, jawabannya adalah TIDAK. Bagi mereka yang tidak kesenangan Bekerja dengan XML di Java (ya, masih hidup) org.w3c.dom Paket tidak aman. Tidak ada jaminan, bahkan jika data dokumen XML hanya dibaca di lingkungan multi-utas.
Untuk latar belakang lebih lanjut, ini adalah utas overflow tumpukan yang bagus tentang topik ini.
Jadi jika Anda memutuskan untuk berlari children() Metode berjalan seiring, cepat atau lambat, Anda akan menemukan sesuatu seperti ini:
Caused by:java.lang.NullPointerException
at java.xml/com.sun.org.apache.xerces.internal.dom.CoreDocumentImpl.getNodeListCache(CoreDocumentImpl.java:2283)
at java.xml/com.sun.org.apache.xerces.internal.dom.ParentNode.nodeListGetLength(ParentNode.java:690)
at java.xml/com.sun.org.apache.xerces.internal.dom.ParentNode.getLength(ParentNode.java:720)
at SafeXml.children(SafeXml.java)
(Java menggunakan Xerces sebagai implementasi default dari API DOM.)
Mari kita perbaiki?
Contents
Coba #1: Penyelamatan Sinkron?
Sebagai pengembang yang berpengalaman, kami melakukan yang terbaik: Google itu. Kami menemukan jawaban ini di StackOverflow:
Satu -satunya pilihan Anda adalah menyinkronkan semua akses ke dokumen/node
Oke, mari kita ikuti sarannya dan tambahkan beberapa sinkronisasi:
public Stream<SafeXml> children(){
synchronized (this.node){
NodeList nodes = this.node.getChildNodes();
int length = nodes.getLength();
return Stream.iterate(0, idx -> idx + 1)
.limit(length)
.map(nodes::item)
.map(SafeXml::new);
}
}
Besar! Sekarang mari kita mengujinya – beberapa panggilan utas children() secara paralel. Tes lulus … sampai lari ke -269, NullPointerException Muncul lagi. Tunggu, apa? Lai Kami tidak hanya Tambahkan sinkronisasi?
Coba #2: Sinkronisasi Dokumen
Ternyata setiap dom Node Memiliki keadaan internal bersama (mungkin cache) Node Contoh. Dengan kata lain, sinkronisasi pada node tidak cukup – kita harus mengunci
Seluruh dokumen.
Jadi kami menyesuaikan kode:
public final class SafeXml {
private final Document doc;
private final Node node;
SafeXml(final Document doc, final Node node) {
this.doc = doc;
this.node = node.cloneNode(true);
}
public Stream<SafeXml> children() {
synchronized (this.doc) {
NodeList nodes = this.node.getChildNodes();
int length = nodes.getLength();
return Stream.iterate(0, idx -> idx + 1)
.limit(length)
.map(nodes::item)
.map(n -> new SafeXml(this.doc, n));
}
}
}
Kami mungkin tidak menyukai solusi ini, tetapi ini adalah satu -satunya cara untuk membuatnya bekerja. DOM API dirancang untuk bacaan baris tunggal, membuatnya aman-aman akan memperkenalkan overhead kinerja yang penting untuk aplikasi satu threaded. Jadi, ini adalah trade-off yang harus kita jalani.
Akhirnya saya menguji lagi dan mereka lewat … sampai lari ke -5518, ketika saya dipukul NullPointerException. Dengan serius? Bagaimana dengan sekarang?
Coba #3: Stream API Knockback
Masalah ini disembunyikan:
return Stream.iterate(0, idx -> idx + 1)
.limit(length)
.map(nodes::item)
.map(n->new SafeXml(this.doc,n));
Saya suka Java Stream API – elegan, ringkas dan kuat. Tapi itu juga Malasyang berarti iterasi yang sebenarnya tidak akan terjadi children() metode. Sebaliknya, ketika operasi terminal benar -benar mengonsumsi aliran, itu terjadi kemudian (mis. collect()) dipanggil. Ini berarti bahwa pada saat iterasi aktual terjadi, sinkronisasi tidak lagi berlaku.
Jadi, apa solusinya? Evaluasi segera sebelum kembali ke aliran. Berikut ini adalah:
Opsi 1: Kumpulkan daftar
return Stream.iterate(0, idx -> idx + 1)
.limit(length)
.map(nodes::item)
.map(n->new SafeXml(this.doc,n))
.collect(Collectors.toList()) // Here we force eager evaluation
.stream(); // and return a new stream
Opsi 2: Gunakan stream.builder
public Stream<SafeXml> children(){
synchronized (this.doc){
NodeList nodes = this.node.getChildNodes();
int length = nodes.getLength();
Stream.Builder<SafeXml> builder = Stream.builder();
for(int i = 0; i < length; i++){
Node n = nodes.item(i);
builder.accept(new SafeXml(this.doc,n));
}
return builder.build();
}
}
Opsi 3: Gunakan ArrayList
public Stream<SafeXml> children(){
synchronized (this.doc){
NodeList nodes = this.node.getChildNodes();
int length = nodes.getLength();
List<SafeXml> children = new ArrayList<>(length);
for(int i = 0; i < length; i++){
Node n = nodes.item(i);
children.add(new SafeXml(this.doc,n));
}
return children.stream();
}
}
Menurut tes benchmark Stream.Builder Metode ini adalah yang tercepat:
| Benchmark | model | Pecahan | satuan |
|---|---|---|---|
| Safexmlbench.array | Avgt | 0.780 | Amerika Serikat/Op |
| safexmlbench.collect_stream | Avgt | 1.360 | Amerika Serikat/Op |
| Safexmlbench.stream_builder | Avgt | 1.360 | Amerika Serikat/Op |
* US/OP – mikrodetik per operasi
Pemikiran terakhir
Jadi kami akhirnya menyelesaikan masalah. Keuntungan utama:
- Node dom tidak aman. Jika Anda perlu memprosesnya secara paralel, sinkronkan di seluruh dokumen.
- Java Stream malas. Jika Anda menggunakannya dalam konteks multithreaded, berhati -hatilah untuk mengevaluasi lokasi mereka.
- Tes ini adalah sahabat Anda, tetapi kadang -kadang melewati 5000 kali sebelum gagal.
Ini bukan hanya masalah teoretis – masalah yang tepat ini ditemukan dan diselesaikan dalam proyek nyata.
Jika Anda penasaran, Anda dapat memeriksa perbaikan yang sebenarnya dalam permintaan tarik ini.
Happy Coding! Dan waspadalah terhadap aliran malas itu!
rencana pengembangan website
metode pengembangan website
jelaskan beberapa rencana untuk pengembangan website, proses pengembangan website, kekuatan dan kelemahan bisnis pengembangan website
, jasa pengembangan website, tahap pengembangan website, biaya pengembangan website
#Perangkap #ThreadSafe #dalam #Pemrosesan #XML