JavaRush /Blog Java /Random-MS /A Tale of Two Iterator: Strategi Pengubahsuaian Kompetiti...

A Tale of Two Iterator: Strategi Pengubahsuaian Kompetitif di Jawa

Diterbitkan dalam kumpulan
Penulis nota itu ialah Grzegorz Mirek, pembangun perisian dari Krakow (Poland). Dia mula membangun di Jawa kira-kira 6 tahun lalu, ketika masih di universiti, dan sejak itu dia tidak jemu menggilap kemahirannya dalam bidang ini. Beliau amat berminat dengan prestasi dan pengoptimuman JVM, yang merupakan perkara yang sering beliau tulis di blognya .
A Tale of Two Iterator: Strategi Pengubahsuaian Kompetitif di Jawa - 1
Beberapa soalan temu bual Java yang paling popular termasuk: Apakah perbezaan antara iterator gagal-cepat dan selamat gagal? Jawapan yang paling mudah untuk ini ialah: Peulang yang gagal-cepat membuang ConcurrentModificationException jika koleksi berubah semasa lelaran, tetapi lelaran yang gagal-selamat tidak. Walaupun ini kedengaran agak bermakna, masih tidak jelas apa yang dimaksudkan oleh penemuduga sebagai fail-safe? Spesifikasi Bahasa Java tidak mentakrifkan istilah ini berkaitan dengan iterator. Walau bagaimanapun, terdapat empat strategi pengubahsuaian kompetitif.

Pengubahsuaian kompetitif

Mula-mula, mari kita tentukan apa itu pengubahsuaian kompetitif (atau selari). Katakan kita mempunyai koleksi dan apabila iterator aktif, beberapa perubahan berlaku yang tidak datang dari iterator ini. Dalam kes ini, kami mendapat pengubahsuaian kompetitif. Biar saya berikan anda contoh mudah: katakan kita mempunyai beberapa utas. Benang pertama berulang, dan benang kedua memasukkan atau mengalih keluar elemen daripada koleksi yang sama. Walau bagaimanapun, kita boleh mendapatkan ConcurrentModificationException apabila berjalan dalam persekitaran satu benang:
List<String> cities = new ArrayList<>();
cities.add(Warsaw);
cities.add(Prague);
cities.add(Budapest);

Iterator<String> cityIterator = cities.iterator();
cityIterator.next();
cities.remove(1);
cityIterator.next(); // генерирует ConcurrentModificationException

Fail-fast

Serpihan kod di atas ialah contoh lelaran yang gagal-cepat . Seperti yang anda lihat, ConcurrentModificationException telah dilemparkan apabila cuba mendapatkan semula elemen kedua daripada iterator . Bagaimanakah iterator mengetahui bahawa koleksi telah diubah suai sejak ia dicipta? Sebagai contoh, koleksi mungkin mempunyai cap tarikh/masa, katakan lastModified . Apabila membuat iterator, anda harus menyalin medan ini dan menyimpannya dalam objek iterator. Kemudian, setiap kali anda memanggil kaedah next() , anda hanya akan membandingkan nilai lastModified daripada koleksi dengan salinan daripada iterator. Pendekatan yang hampir sama digunakan, sebagai contoh, dalam pelaksanaan kelas ArrayList . Ia mempunyai modCount pembolehubah contoh yang menyimpan bilangan kali senarai telah diubah suai:
final void checkForComodification() {
   if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
}
Adalah penting untuk ambil perhatian bahawa iterator yang gagal-cepat beroperasi pada asas baka terbaik, bermakna tiada jaminan bahawa ConcurrentModificationException akan dilemparkan sekiranya pengubahsuaian serentak. Oleh itu, anda tidak sepatutnya bergantung pada mereka - sebaliknya, ia harus digunakan untuk mengesan ralat. Kebanyakan koleksi bukan serentak menyediakan iterator yang gagal .

Konsistensi Lemah

Kebanyakan koleksi serentak dalam pakej java.util.concurrent (seperti ConcurrentHashMap dan kebanyakan Queue ) menyediakan iterator yang konsisten lemah. Maksud istilah ini dijelaskan dengan baik dalam dokumentasi :
  • Mereka boleh diproses serentak dengan operasi lain
  • Mereka tidak pernah membuang ConcurrentModificationException
  • Mereka dijamin melintasi elemen sedia ada pada masa iterator dibuat tepat sekali, dan boleh (tetapi tidak diperlukan untuk) mencerminkan pengubahsuaian berikutnya.

Syot kilat

Dengan strategi ini, iterator dikaitkan dengan keadaan koleksi pada masa penciptaannya - ini ialah petikan koleksi. Sebarang perubahan yang dibuat pada koleksi asal mengakibatkan penciptaan versi baharu struktur data asas. Ini membiarkan syot kilat kami tidak berubah, jadi ia tidak menggambarkan perubahan pada koleksi yang berlaku selepas lelaran dibuat. Ini adalah teknik copy-on-write (COW) lama yang bagus . Ia menyelesaikan sepenuhnya masalah pengubahsuaian serentak, jadi ConcurrentModificationException tidak dihasilkan dengan pendekatan ini. Selain itu, iterator tidak menyokong operasi yang mengubah elemen. Koleksi salin-tulis cenderung terlalu mahal untuk digunakan, tetapi masuk akal untuk menggunakannya jika perubahan berlaku lebih kurang kerap daripada traversal lelaran. Contohnya ialah kelas CopyOnWriteArrayList dan CopyOnWriteArraySet .

Tingkah laku yang tidak ditentukan

Anda mungkin menghadapi gelagat yang tidak ditentukan dalam jenis koleksi warisan seperti Vektor dan Hashtable . Kedua-duanya mempunyai iterator fail-fast standard , tetapi sebagai tambahan, mereka membenarkan penggunaan pelaksanaan antara muka Enumeration , dan mereka tidak tahu cara berkelakuan sekiranya berlaku pengubahsuaian serentak. Anda mungkin menghadapi beberapa elemen berulang atau hilang, atau beberapa pengecualian pelik. Lebih baik jangan bermain dengan mereka!
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION