Sebagai alat untuk berbagi data secara bersamaan dan memastikan konsistensi, kunci memiliki banyak implementasi pada platform JAVA (seperti tersinkronisasi dan ReentrantLock, dll.). Kunci yang sudah tertulis ini memberikan kemudahan bagi pengembangan kami, namun sifat spesifik dan jenis kunci jarang disebutkan. Rangkaian artikel ini akan menganalisis nama dan karakteristik kunci umum di JAVA untuk menjawab pertanyaan Anda.
1. Putar kunci
Spin lock diimplementasikan dengan mengizinkan thread saat ini untuk terus dieksekusi di dalam badan loop. Hanya ketika kondisi loop diubah oleh thread lain barulah bagian kritis dapat dimasukkan. Salin kode sebagai berikut:
SpinLock kelas publik {
tanda AtomicReference<Thread> pribadi = AtomicReference baru<>();
kunci kekosongan publik(){
Thread saat ini = Thread.currentThread();
while(!sign .compareAndSet(null, saat ini)){
}
}
buka kunci kekosongan publik (){
Thread saat ini = Thread.currentThread();
tanda tangani .compareAndSet(saat ini, null);
}
}
Menggunakan operasi atom CAS, fungsi kunci menetapkan pemilik ke thread saat ini dan memprediksi bahwa nilai asli kosong. Fungsi buka kunci menyetel pemilik ke nol, dan nilai prediksinya adalah thread saat ini.
Ketika thread kedua memanggil operasi kunci, karena nilai pemilik tidak kosong, loop dijalankan hingga thread pertama memanggil fungsi buka kunci untuk menyetel pemilik ke null, dan thread kedua dapat memasuki bagian kritis.
Karena spin lock hanya menjaga thread saat ini mengeksekusi badan loop tanpa mengubah status thread, kecepatan responsnya lebih cepat. Namun ketika jumlah thread terus bertambah, kinerja turun secara signifikan karena setiap thread perlu dijalankan dan memakan waktu CPU. Jika persaingan benang tidak ketat dan kuncian dipertahankan untuk jangka waktu tertentu. Cocok untuk digunakan dengan kunci putar.
Catatan: Contoh ini adalah kunci yang tidak adil. Urutan mendapatkan kunci tidak akan didasarkan pada urutan memasukkan kunci.
2. Jenis kunci putar lainnya
Kita telah membicarakan tentang spin lock di atas. Ada tiga bentuk kunci umum dalam spin lock: TicketLock, CLHlock, dan MCSlock.
Kunci tiket terutama memecahkan masalah urutan akses. Masalah utama ada pada CPU multi-core:
Copy kode kodenya sebagai berikut:
paket com.alipay.titan.dcc.dal.entity;
import java.util.concurrent.atomic.AtomicInteger;
TicketLock kelas publik {
private AtomicInteger serviceNum = new AtomicInteger();
tiketNum AtomicInteger pribadi = AtomicInteger baru();
private static final ThreadLocal<Integer> LOCAL = new ThreadLocal<Integer>();
kunci kekosongan publik() {
int tiket saya = ticketNum.getAndIncrement();
LOKAL.set(tiket saya);
while (tiket saya!= serviceNum.get()) {
}
}
buka kunci kekosongan publik() {
int tiket saya = LOKAL.get();
serviceNum.compareAndSet(tiket saya, tiket saya + 1);
}
}
Nomor layanan serviceNum harus ditanyakan setiap saat, yang mempengaruhi kinerja (harus dibaca dari memori utama dan CPU lain harus dilarang memodifikasinya).
CLHLock dan MCSLock adalah dua jenis kunci adil yang serupa, diurutkan dalam bentuk daftar tertaut.
Copy kode kodenya sebagai berikut:
impor java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
CLHLock kelas publik {
CLHNode kelas statis publik {
boolean volatil pribadi isLocked = true;
}
@SuppressWarnings("tidak digunakan")
ekor CLHNode pribadi yang mudah menguap;
private static final ThreadLocal<CLHNode> LOCAL = new ThreadLocal<CLHNode>();
final statis pribadi AtomicReferenceFieldUpdater<CLHLock, CLHNode> UPDATER = AtomicReferenceFieldUpdater.newUpdater(CLHLock.class,
CLHNode.kelas, "ekor");
kunci kekosongan publik() {
simpul CLHNode = CLHNode baru();
LOKAL.set(simpul);
CLHNode preNode = UPDATER.getAndSet(ini, simpul);
jika (preNode != null) {
while (preNode.isLocked) {
}
simpul awal = null;
LOKAL.set(simpul);
}
}
buka kunci kekosongan publik() {
simpul CLHNode = LOKAL.get();
if (!UPDATER.compareAndSet(ini, simpul, null)) {
node.isLocked = salah;
}
simpul = nol;
}
}
CLHlock terus-menerus menanyakan variabel prekursor, sehingga tidak cocok untuk digunakan dalam arsitektur NUMA (dalam arsitektur ini, setiap thread didistribusikan di area memori fisik yang berbeda)
MCSLock melakukan loop melalui node variabel lokal. Tidak ada masalah dengan CLHlock.
Copy kode kodenya sebagai berikut:
impor java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
MCSLock kelas publik {
MCSNode kelas statis publik {
MCSNode yang mudah menguap berikutnya;
boolean yang mudah menguap isLocked = true;
}
private static final ThreadLocal<MCSNode> NODE = new ThreadLocal<MCSNode>();
@SuppressWarnings("tidak digunakan")
antrian MCSNode pribadi yang mudah menguap;
final statis pribadi AtomicReferenceFieldUpdater<MCSLock, MCSNode> UPDATER = AtomicReferenceFieldUpdater.newUpdater(MCSLock.class,
MCSNode.class, "antrian");
kunci kekosongan publik() {
MCSNode currentNode = MCSNode baru();
NODE.set(Node saat ini);
MCSNode preNode = UPDATER.getAndSet(ini, currentNode);
jika (preNode != null) {
preNode.next = Node saat ini;
while (Node saat ini.isTerkunci) {
}
}
}
buka kunci kekosongan publik() {
MCSNode currentNode = NODE.get();
if (Node saat ini.berikutnya == null) {
if (UPDATER.compareAndSet(ini, Node saat ini, null)) {
} kalau tidak {
while (Node saat ini.berikutnya == null) {
}
}
} kalau tidak {
currentNode.next.isLocked = salah;
Node saat ini.berikutnya = null;
}
}
}
Dari sudut pandang kode, CLH lebih sederhana dari MCS.
Antrian CLH adalah antrian implisit dan tidak memiliki atribut node penerus yang sebenarnya.
Antrian MCS adalah antrian eksplisit dengan atribut node penerus yang sebenarnya.
Kunci default yang digunakan secara internal oleh JUC ReentrantLock adalah kunci CLH (banyak perbaikan, seperti mengganti spin lock dengan kunci pemblokiran, dll.).
(Teks lengkap berakhir)