Ada dua kegunaan utama AtomicInteger
:
Sebagai penghitung atom ( incrementAndGet()
, dll) yang dapat digunakan oleh banyak utas secara bersamaan
Sebagai primitif yang mendukung instruksi compare-and-swap ( compareAndSet()
) untuk mengimplementasikan algoritma non-blocking.
Berikut adalah contoh generator nomor acak non-blocking dari Java Concurrency In Practice Brian Göetz :
public class AtomicPseudoRandom extends PseudoRandom {
private AtomicInteger seed;
AtomicPseudoRandom(int seed) {
this.seed = new AtomicInteger(seed);
}
public int nextInt(int n) {
while (true) {
int s = seed.get();
int nextSeed = calculateNext(s);
if (seed.compareAndSet(s, nextSeed)) {
int remainder = s % n;
return remainder > 0 ? remainder : remainder + n;
}
}
}
...
}
Seperti yang Anda lihat, ini pada dasarnya bekerja dengan cara yang hampir sama incrementAndGet()
, tetapi melakukan perhitungan sewenang-wenang ( calculateNext()
) alih-alih kenaikan (dan memproses hasilnya sebelum kembali).
read
danwrite that value + 1
, ini terdeteksi daripada menimpa pembaruan lama (menghindari masalah "pembaruan hilang"). Ini sebenarnya adalah kasus khususcompareAndSet
- jika nilai lama adalah2
, kelas sebenarnya memanggilcompareAndSet(2, 3)
- jadi jika utas lain telah mengubah nilai sementara itu, metode kenaikan secara efektif restart dari awal.Contoh paling sederhana mutlak yang dapat saya pikirkan adalah membuat penambahan operasi atom.
Dengan int standar:
Dengan AtomicInteger:
Yang terakhir adalah cara yang sangat sederhana untuk melakukan efek mutasi sederhana (terutama penghitungan, atau pengindeksan unik), tanpa harus menggunakan sinkronisasi semua akses.
Logika bebas sinkronisasi yang lebih kompleks dapat digunakan dengan menggunakan
compareAndSet()
jenis penguncian optimis - dapatkan nilai saat ini, hitung hasil berdasarkan ini, tetapkan hasil ini jika nilai masih input yang digunakan untuk melakukan perhitungan, lain mulai lagi - tetapi menghitung contoh sangat berguna, dan saya akan sering menggunakanAtomicIntegers
penghitungan dan generator unik VM-lebar jika ada beberapa thread terlibat, karena mereka sangat mudah untuk bekerja dengan saya hampir menganggap itu optimasi prematur untuk menggunakan polosints
.Meskipun Anda hampir selalu dapat mencapai jaminan sinkronisasi yang sama dengan
ints
dansynchronized
deklarasi yang sesuai , keindahan dariAtomicInteger
thread-safety dibangun ke dalam objek aktual itu sendiri, daripada Anda perlu khawatir tentang kemungkinan interleavings, dan monitor yang dimiliki, dari setiap metode itu terjadi untuk mengaksesint
nilai. Jauh lebih sulit untuk secara tidak sengaja melanggar keamanan threads saat menelepongetAndIncrement()
daripada ketika kembalii++
dan mengingat (atau tidak) untuk mendapatkan set monitor yang benar sebelumnya.sumber
Jika Anda melihat metode yang dimiliki AtomicInteger, Anda akan melihat bahwa mereka cenderung sesuai dengan operasi umum pada int. Misalnya:
adalah versi yang aman untuk ini:
Metode peta seperti ini:
++i
isi.incrementAndGet()
i++
isi.getAndIncrement()
--i
isi.decrementAndGet()
i--
isi.getAndDecrement()
i = x
isi.set(x)
x = i
isx = i.get()
Ada metode kenyamanan lain juga, seperti
compareAndSet
atauaddAndGet
sumber
Penggunaan utama
AtomicInteger
adalah ketika Anda berada dalam konteks multithreaded dan Anda perlu melakukan operasi aman thread pada integer tanpa menggunakansynchronized
. Penugasan dan pengambilan pada tipe primitifint
sudah bersifat atomik tetapiAtomicInteger
dilengkapi dengan banyak operasi yang bukan atomikint
.Yang paling sederhana adalah
getAndXXX
atauxXXAndGet
. MisalnyagetAndIncrement()
adalah setara atomi++
yang bukan atom karena sebenarnya merupakan jalan pintas untuk tiga operasi: pengambilan, penambahan dan penugasan.compareAndSet
sangat berguna untuk mengimplementasikan semaphores, kunci, kait, dll.Menggunakan
AtomicInteger
lebih cepat dan lebih mudah dibaca daripada melakukan sinkronisasi menggunakan yang sama.Tes sederhana:
Pada PC saya dengan Java 1.6 tes atom berjalan dalam 3 detik sementara yang disinkronkan berjalan dalam sekitar 5,5 detik. Masalahnya di sini adalah bahwa operasi untuk menyinkronkan (
notAtomic++
) sangat singkat. Jadi biaya sinkronisasi sangat penting dibandingkan dengan operasi.Di samping atomicity, AtomicInteger dapat digunakan sebagai versi yang bisa berubah
Integer
misalnya dalamMap
nilai s.sumber
AtomicInteger
sebagai kunci peta, karena menggunakanequals()
implementasi default , yang hampir pasti bukan semantik yang Anda harapkan jika digunakan dalam peta.Sebagai contoh, saya memiliki perpustakaan yang menghasilkan instance dari beberapa kelas. Masing-masing instance harus memiliki ID integer unik, karena instance ini mewakili perintah yang dikirim ke server, dan setiap perintah harus memiliki ID unik. Karena beberapa utas diizinkan untuk mengirim perintah secara bersamaan, saya menggunakan AtomicInteger untuk menghasilkan ID tersebut. Pendekatan alternatif adalah dengan menggunakan semacam kunci dan integer biasa, tapi itu lebih lambat dan kurang elegan.
sumber
Seperti kata gabuzo, kadang-kadang saya menggunakan AtomicIntegers ketika saya ingin melewatkan int dengan referensi. Ini adalah kelas bawaan yang memiliki kode khusus arsitektur, jadi lebih mudah dan cenderung lebih dioptimalkan daripada MutableInteger yang bisa saya kodekan dengan cepat. Yang mengatakan, rasanya seperti penyalahgunaan kelas.
sumber
Di Java 8, kelas atom telah diperluas dengan dua fungsi menarik:
Keduanya menggunakan fungsi update untuk melakukan pembaruan nilai atom. Perbedaannya adalah bahwa yang pertama mengembalikan nilai lama dan yang kedua mengembalikan nilai baru. Fungsi pemutakhiran dapat diterapkan untuk melakukan operasi "bandingkan dan set" yang lebih kompleks daripada yang standar. Sebagai contoh ia dapat memeriksa bahwa penghitung atom tidak mencapai di bawah nol, biasanya itu akan memerlukan sinkronisasi, dan di sini kode bebas dari kunci:
Kode diambil dari Java Atomic Contoh .
sumber
Saya biasanya menggunakan AtomicInteger ketika saya harus memberikan Id ke objek yang dapat diakses atau dibuat dari beberapa utas, dan saya biasanya menggunakannya sebagai atribut statis pada kelas yang saya akses di konstruktor objek.
sumber
Anda dapat menerapkan kunci non-pemblokiran menggunakan compareAndSwap (CAS) pada bilangan bulat atom atau panjang. The "TL2" Software Transaksional Memori kertas menggambarkan ini:
Apa yang digambarkannya adalah pertama membaca bilangan bulat atom. Bagi ini menjadi bit kunci yang diabaikan dan nomor versi. Mencoba untuk CAS menulisnya sebagai bit kunci dibersihkan dengan nomor versi saat ini ke set bit kunci dan nomor versi berikutnya. Ulangi sampai Anda berhasil dan Anda adalah utas yang memiliki kunci. Buka kunci dengan mengatur nomor versi saat ini dengan kunci-bit dihapus. Makalah ini menjelaskan penggunaan nomor versi dalam kunci untuk mengoordinasikan bahwa utas memiliki rangkaian pembacaan yang konsisten ketika mereka menulis.
Artikel ini menjelaskan bahwa prosesor memiliki dukungan perangkat keras untuk membandingkan dan menukar operasi yang membuatnya sangat efisien. Ia juga mengklaim:
sumber
Kuncinya adalah mereka memungkinkan akses bersamaan dan modifikasi dengan aman. Mereka umumnya digunakan sebagai penghitung di lingkungan multithreaded - sebelum pengenalan mereka ini harus menjadi kelas tertulis pengguna yang membungkus berbagai metode dalam blok yang disinkronkan.
sumber
Saya menggunakan AtomicInteger untuk memecahkan masalah Dining Philosopher.
Dalam solusi saya, contoh AtomicInteger digunakan untuk mewakili garpu, ada dua yang diperlukan per filsuf. Setiap filsuf diidentifikasi sebagai bilangan bulat, 1 sampai 5. Ketika garpu digunakan oleh seorang filsuf, AtomicInteger memegang nilai filsuf, 1 sampai 5, jika garpu tidak digunakan sehingga nilai AtomicInteger adalah -1 .
AtomicInteger kemudian memungkinkan untuk memeriksa apakah garpu bebas, nilai == - 1, dan mengaturnya ke pemilik garpu jika gratis, dalam satu operasi atom. Lihat kode di bawah ini.
Karena metode compareAndSet tidak memblokir, itu harus meningkatkan throughput, lebih banyak pekerjaan yang dilakukan. Seperti yang Anda ketahui, masalah Makan Filsuf digunakan ketika terkontrol diakses ke sumber daya diperlukan, yaitu garpu, diperlukan, seperti proses membutuhkan sumber daya untuk terus melakukan pekerjaan.
sumber
Contoh sederhana untuk fungsi compareAndSet ():
Dicetak adalah: nilai sebelumnya: 0 Nilai telah diperbarui dan itu adalah 6 Contoh sederhana lainnya:
Dicetak adalah: Nilai sebelumnya: 0 Nilai tidak diperbarui
sumber