Apa yang dilakukan AtomicBoolean yang tidak dapat dicapai oleh boolean yang mudah menguap?
Apa yang dilakukan AtomicBoolean yang tidak dapat dicapai oleh boolean yang mudah menguap?
Jawaban:
Mereka sama sekali berbeda. Pertimbangkan contoh volatile
bilangan bulat ini:
volatile int i = 0;
void incIBy5() {
i += 5;
}
Jika dua utas memanggil fungsi secara bersamaan, i
mungkin 5 setelahnya, karena kode yang dikompilasi akan agak mirip dengan ini (kecuali Anda tidak dapat menyinkronkan aktif int
):
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
Jika suatu variabel volatil, setiap akses atom ke sana disinkronkan, tetapi tidak selalu jelas apa yang sebenarnya memenuhi syarat sebagai akses atom. Dengan sebuah Atomic*
objek, dijamin bahwa setiap metode adalah "atom".
Jadi, jika Anda menggunakan AtomicInteger
dan getAndAdd(int delta)
, Anda dapat yakin bahwa hasilnya akan 10
. Dengan cara yang sama, jika dua utas keduanya meniadakan boolean
variabel secara bersamaan, dengan AtomicBoolean
Anda dapat yakin itu memiliki nilai asli setelahnya, dengan volatile boolean
, Anda tidak bisa.
Jadi, setiap kali Anda memiliki lebih dari satu utas memodifikasi bidang, Anda harus membuatnya atomik atau menggunakan sinkronisasi eksplisit.
Tujuannya volatile
berbeda. Pertimbangkan contoh ini
volatile boolean stop = false;
void loop() {
while (!stop) { ... }
}
void stop() { stop = true; }
Jika Anda memiliki utas berjalan loop()
dan utas lain memanggil stop()
, Anda mungkin mengalami infinite loop jika Anda hilangkan volatile
, karena utas pertama mungkin men-cache nilai stop. Di sini, volatile
berfungsi sebagai petunjuk bagi kompiler untuk sedikit lebih berhati-hati dengan optimisasi.
volatile
. Pertanyaannya adalah tentang volatile boolean
vs AtomicBoolean
.
volatile boolean
sudah cukup. Jika ada banyak penulis, Anda mungkin perlu AtomicBoolean
.
Saya menggunakan bidang volatil ketika bidang tersebut HANYA DIPERBARUI oleh utas pemiliknya dan nilainya hanya dibaca oleh utas lainnya, Anda dapat menganggapnya sebagai skenario terbitkan / berlangganan di mana terdapat banyak pengamat tetapi hanya satu penerbit. Namun jika pengamat tersebut harus melakukan beberapa logika berdasarkan pada nilai bidang dan kemudian mendorong kembali nilai baru maka saya pergi dengan vars Atomic atau kunci atau blok yang disinkronkan, apa pun yang paling cocok untuk saya. Dalam banyak skenario bersamaan, intinya adalah untuk mendapatkan nilai, membandingkannya dengan yang lain dan memperbarui jika perlu, karenanya metode compareAndSet dan getAndSet hadir di kelas Atomic *.
Periksa JavaDocs dari paket java.util.concurrent.atomic untuk daftar kelas Atom dan penjelasan yang sangat baik tentang cara kerjanya (baru mengetahui bahwa mereka bebas kunci, sehingga mereka memiliki keunggulan dibandingkan kunci atau blok yang disinkronkan)
boolean
var, kita harus memilih volatile boolean
.
Anda tidak bisa melakukannya compareAndSet
, getAndSet
karena operasi atom dengan volatile boolean (kecuali tentu saja Anda menyinkronkannya).
AtomicBoolean
memiliki metode yang melakukan operasi senyawa mereka secara atom dan tanpa harus menggunakan synchronized
blok. Di samping itu,volatile boolean
hanya dapat melakukan operasi gabungan jika dilakukan dalam synchronized
blok.
Efek memori dari membaca / menulis volatile boolean
identik dengan get
dan set
metodeAtomicBoolean
masing masing.
Misalnya compareAndSet
metode akan melakukan hal berikut secara atomis (tanpa synchronized
blok):
if (value == expectedValue) {
value = newValue;
return true;
} else {
return false;
}
Oleh karena itu, compareAndSet
metode ini akan membiarkan Anda menulis kode yang dijamin untuk dieksekusi hanya sekali, bahkan ketika dipanggil dari banyak utas. Sebagai contoh:
final AtomicBoolean isJobDone = new AtomicBoolean(false);
...
if (isJobDone.compareAndSet(false, true)) {
listener.notifyJobDone();
}
Dijamin hanya akan memberi tahu pendengar sekali (dengan asumsi tidak ada utas lain yang mengatur AtomicBoolean
kembali false
lagi setelah disetel ke true
).
volatile
jaminan kata kunci terjadi sebelum hubungan antara utas yang berbagi variabel itu. Itu tidak menjamin Anda bahwa 2 utas atau lebih tidak akan saling mengganggu saat mengakses variabel boolean itu.
Atomic*
kelas membungkus volatile
lapangan.
Volatile boolean vs AtomicBoolean
Kelas Atomic membungkus primitif yang mudah menguap dari jenis yang sama. Dari sumber:
public class AtomicLong extends Number implements java.io.Serializable {
...
private volatile long value;
...
public final long get() {
return value;
}
...
public final void set(long newValue) {
value = newValue;
}
Jadi jika semua yang Anda lakukan adalah mendapatkan dan mengatur Atom * maka Anda mungkin hanya memiliki bidang yang mudah menguap.
Apa yang dilakukan AtomicBoolean yang tidak dapat dicapai oleh boolean yang mudah menguap?
Kelas Atom * memberi Anda metode yang memberikan fungsionalitas lebih lanjut seperti incrementAndGet()
, compareAndSet()
, dan lain-lain yang mengimplementasikan beberapa operasi (get / increment / set, uji / set) tanpa penguncian. Itu sebabnya kelas * Atom sangat kuat.
Misalnya, jika beberapa utas menggunakan kode berikut ++
, akan ada kondisi balapan karena ++
sebenarnya: dapatkan, tambah, dan tetapkan.
private volatile value;
...
// race conditions here
value++;
Namun, kode berikut akan bekerja di lingkungan multi-utas dengan aman tanpa kunci:
private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();
Penting juga untuk dicatat bahwa membungkus bidang volatil Anda menggunakan kelas Atom * adalah cara yang baik untuk merangkum sumber daya kritis bersama dari sudut pandang objek. Ini berarti bahwa pengembang tidak bisa hanya berurusan dengan bidang dengan asumsi itu tidak dibagikan kemungkinan menyuntikkan masalah dengan bidang ++; atau kode lain yang memperkenalkan kondisi lomba.
Jika ada beberapa utas yang mengakses variabel level kelas maka setiap utas dapat menyimpan salinan variabel itu dalam cache threadlocal-nya.
Membuat variabel volatile akan mencegah thread menyimpan salinan variabel dalam cache threadlocal.
Variabel atom berbeda dan mereka memungkinkan modifikasi atom dari nilainya.
Tipe primitif Boolean adalah atom untuk operasi tulis dan baca, volatile menjamin prinsip yang terjadi sebelum. Jadi jika Anda membutuhkan get () dan set () yang sederhana maka Anda tidak perlu AtomicBoolean.
Di sisi lain, jika Anda perlu menerapkan beberapa pemeriksaan sebelum menetapkan nilai variabel, misalnya "jika benar lalu disetel ke false", maka Anda perlu melakukan operasi ini secara atomik, dalam hal ini gunakan compareAndSet dan metode lain yang disediakan oleh AtomicBoolean, karena jika Anda mencoba menerapkan logika ini dengan volatile boolean Anda akan memerlukan beberapa sinkronisasi untuk memastikan bahwa nilainya tidak berubah antara get dan set.
Ingat IDIOM -
BACA - MODIFIKASI - MENULIS ini Anda tidak dapat mencapai dengan volatile
volatile
hanya berfungsi dalam kasus di mana utas pemilik memiliki kemampuan untuk memperbarui nilai bidang dan utas lainnya hanya bisa membaca.
Jika Anda hanya memiliki satu utas memodifikasi boolean Anda, Anda dapat menggunakan boolean yang mudah menguap (biasanya Anda melakukan ini untuk menentukanstop
variabel yang dicentang di loop utama utas).
Namun, jika Anda memiliki banyak utas memodifikasi boolean, Anda harus menggunakan AtomicBoolean
. Selain itu, kode berikut tidak aman:
boolean r = !myVolatileBoolean;
Operasi ini dilakukan dalam dua langkah:
Jika utas lain mengubah nilai antara #1
dan 2#
, Anda mungkin mendapat hasil yang salah. AtomicBoolean
metode menghindari masalah ini dengan melakukan langkah-langkah #1
dan #2
secara atomis.
Keduanya memiliki konsep yang sama tetapi dalam boolean atom itu akan memberikan atomicity untuk operasi jika cpu switch terjadi di antaranya.