1. Gambaran keseluruhan
Kerangka koleksi adalah komponen utama Java. Ini menyediakan sebilangan besar antara muka dan implementasi, yang memungkinkan kita membuat dan memanipulasi pelbagai jenis koleksi dengan cara yang mudah.
Walaupun menggunakan koleksi biasa yang tidak disinkronkan secara sederhana, ia juga boleh menjadi proses yang menakutkan dan ralat ketika bekerja di persekitaran berbilang benang (aka pengaturcaraan serentak).
Oleh itu, platform Java memberikan sokongan yang kuat untuk senario ini melalui pembungkus penyegerakan yang berbeza yang dilaksanakan dalam kelas Koleksi .
Pembungkus ini menjadikannya mudah untuk membuat paparan segerak koleksi yang disediakan dengan menggunakan beberapa kaedah kilang statik.
Dalam tutorial ini, kita akan menyelami pembungkus penyegerakan statik ini . Juga, kami akan mengetengahkan perbezaan antara koleksi yang disegerakkan dan koleksi serentak .
2. synchronizedCollection () Menghubungi
Pembungkus penyegerakan pertama yang akan kita bahas dalam pusingan ini adalah kaedah synchronizedCollection () . Seperti namanya, ia mengembalikan koleksi yang selamat dari benang yang disokong oleh Koleksi yang ditentukan .
Sekarang, untuk memahami dengan lebih jelas cara menggunakan kaedah ini, mari buat ujian unit asas:
Collection syncCollection = Collections.synchronizedCollection(new ArrayList()); Runnable listOperations = () -> { syncCollection.addAll(Arrays.asList(1, 2, 3, 4, 5, 6)); }; Thread thread1 = new Thread(listOperations); Thread thread2 = new Thread(listOperations); thread1.start(); thread2.start(); thread1.join(); thread2.join(); assertThat(syncCollection.size()).isEqualTo(12); }
Seperti yang ditunjukkan di atas, membuat paparan segerak koleksi yang disediakan dengan kaedah ini sangat mudah.
Untuk menunjukkan bahawa kaedah ini benar-benar mengembalikan koleksi yang selamat untuk utas, pertama-tama kami membuat beberapa utas.
Selepas itu, kami kemudian memasukkan contoh Runnable ke dalam konstruktor mereka, dalam bentuk ungkapan lambda. Ingatlah bahawa Runnable adalah antara muka yang berfungsi, jadi kita boleh menggantinya dengan ungkapan lambda.
Terakhir, kami hanya memeriksa bahawa setiap utas menambahkan enam elemen dengan berkesan pada koleksi yang disegerakkan, jadi ukuran terakhirnya adalah dua belas.
3. synchronizedList () Menghubungi
Begitu juga, serupa dengan synchronizedCollection () kaedah, kita boleh menggunakan synchronizedList () wrapper untuk mewujudkan disegerakkan Senarai .
Seperti yang kita jangkakan, kaedah yang mengembalikan pandangan thread-selamat yang dinyatakan Senarai :
List syncList = Collections.synchronizedList(new ArrayList());
Tidak menghairankan, penggunaan kaedah SynchronizedList () kelihatan hampir sama dengan rakan sejawatnya yang lebih tinggi, synchronizedCollection () .
Oleh itu, seperti yang kita hanya lakukan dalam ujian unit sebelumnya, sekali bahawa kami telah membuat disegerakkan Senarai , kita boleh bertelur beberapa benang. Setelah melakukan itu, kami akan menggunakannya untuk mengakses / memanipulasi Senarai sasaran secara selamat.
Sebagai tambahan, jika kita ingin melakukan pengulangan pada koleksi yang disegerakkan dan mencegah hasil yang tidak dijangka, kita harus secara eksplisit memberikan pelaksanaan loop yang selamat untuk thread kita sendiri. Oleh itu, kita dapat mencapainya menggunakan blok yang disegerakkan :
List syncCollection = Collections.synchronizedList(Arrays.asList("a", "b", "c")); List uppercasedCollection = new ArrayList(); Runnable listOperations = () -> { synchronized (syncCollection) { syncCollection.forEach((e) -> { uppercasedCollection.add(e.toUpperCase()); }); } };
Dalam semua kes di mana kita perlu melakukan iterasi pada koleksi yang disegerakkan, kita harus menerapkan ungkapan ini. Ini kerana lelaran pada koleksi yang diselaraskan dilakukan melalui beberapa panggilan ke koleksi. Oleh itu, ia perlu dilakukan sebagai operasi atom tunggal.
Penggunaan blok yang disegerakkan memastikan keberkesanan operasi .
4. synchronizedMap () Menghubungi
The Collections melaksanakan kelas yang lain penyegerakan wrapper kemas, dipanggil synchronizedMap (). Kami dapat menggunakannya untuk membuat Peta yang disegerakkan dengan mudah .
Kaedah ini mengembalikan pandangan selamat dari pelaksanaan Peta yang disediakan :
Map syncMap = Collections.synchronizedMap(new HashMap());
5. synchronizedSortedMap () Menghubungi
Terdapat juga kaedah pelaksanaan kaedah synchronizedMap () . Ia disebut synchronizedSortedMap () , yang dapat kita gunakan untuk membuat instance SortedMap yang diselaraskan :
Map syncSortedMap = Collections.synchronizedSortedMap(new TreeMap());
6. synchronizedSet () Menghubungi
Seterusnya, meneruskan kajian ini, kita mempunyai kaedah synchronizedSet () . Seperti namanya, ini membolehkan kita membuat Set yang diselaraskan dengan sedikit kekecohan.
Pembungkus mengembalikan koleksi selamat utas yang disokong oleh Set yang ditentukan :
Set syncSet = Collections.synchronizedSet(new HashSet());
7. synchronizedSortedSet () Menghubungi
Akhirnya, pembungkus penyegerakan terakhir yang akan kami paparkan di sini adalah synchronizedSortedSet () .
Sama seperti implementasi pembungkus lain yang telah kami kaji sejauh ini, kaedah ini mengembalikan versi selamat dari benang dari SortedSet yang diberikan :
SortedSet syncSortedSet = Collections.synchronizedSortedSet(new TreeSet());
8. Koleksi Bersamaan vs Bersamaan
Hingga tahap ini, kami melihat lebih dekat pembungkus penyegerakan kerangka koleksi.
Now, let's focus on the differences between synchronized collections and concurrent collections, such as ConcurrentHashMap and BlockingQueue implementations.
8.1. Synchronized Collections
Synchronized collections achieve thread-safety through intrinsic locking, and the entire collections are locked. Intrinsic locking is implemented via synchronized blocks within the wrapped collection's methods.
As we might expect, synchronized collections assure data consistency/integrity in multi-threaded environments. However, they might come with a penalty in performance, as only one single thread can access the collection at a time (a.k.a. synchronized access).
For a detailed guide on how to use synchronized methods and blocks, please check our article on the topic.
8.2. Concurrent Collections
Concurrent collections (e.g. ConcurrentHashMap), achieve thread-safety by dividing their data into segments. In a ConcurrentHashMap, for example, different threads can acquire locks on each segment, so multiple threads can access the Map at the same time (a.k.a. concurrent access).
Concurrent collections are much more performant than synchronized collections, due to the inherent advantages of concurrent thread access.
So, the choice of what type of thread-safe collection to use depends on the requirements of each use case, and it should be evaluated accordingly.
9. Kesimpulannya
Dalam artikel ini, kami melihat sekumpulan pembungkus penyegerakan yang dilaksanakan dalam kelas Koleksi .
Selain itu, kami menyoroti perbezaan antara koleksi yang diselaraskan dan bersamaan, dan juga melihat pendekatan yang mereka laksanakan untuk mencapai keselamatan benang.
Seperti biasa, semua contoh kod yang ditunjukkan dalam artikel ini terdapat di GitHub.