Jika perpustakaan kelas Java tidak menyediakan alat sinkronisasi yang sesuai, Anda perlu membuat alat sinkronisasi khusus.
Struktur untuk memblokir operasi yang bergantung pada negara
Copy kode kodenya sebagai berikut:
memperoleh kunci pada status objek;//Permintaan untuk memperoleh kunci
while(prakondisi tidak berlaku){//Prakondisi tidak terpenuhi
lepaskan kunci;//lepaskan kunci terlebih dahulu
tunggu hingga prasyarat terpenuhi; //tunggu hingga prasyarat terpenuhi
opsional gagal jika terputus atau batas waktu habis;//Eksekusi gagal karena gangguan atau batas waktu habis
mendapatkan kembali kunci;//Coba lagi untuk mendapatkan kunci
}
melakukan tindakan//jalankan
kunci pelepas;//kunci pelepas
Contoh kelas dasar implementasi cache yang dibatasi
Copy kode kodenya sebagai berikut:
kelas publik BaseBoundBuffer<V> {
penggemar V[] akhir pribadi;
ekor int pribadi;
kepala int pribadi;
jumlah int pribadi;
@SuppressWarnings("tidak dicentang")
publik BaseBoundBuffer(int kapasitas) {
buf = (V[]) Objek baru[kapasitas];
}
void doPut yang disinkronkan publik(V v) {
buf[ekor] = v;
if (++tail == buf.panjang)
ekor = 0;
hitungan++;
}
V doTake() yang disinkronkan secara publik() {
V v = buf[kepala];
if (++head == buf.panjang)
kepala = 0;
menghitung--;
kembali v;
}
boolean tersinkronisasi akhir publik isFull() {
jumlah pengembalian == buf.length;
}
boolean tersinkronisasi akhir publik isEmpty() {
jumlah pengembalian == 0;
}
}
Memblokir metode implementasi 1: Melemparkan pengecualian ke pemanggil
Copy kode kodenya sebagai berikut:
void put1(V v) yang disinkronkan secara publik melempar Pengecualian{
jika(sudah penuh())
melempar Pengecualian baru("kesalahan penuh");
lakukanPut(v);
}
Analisis: Pengecualian harus digunakan ketika pengecualian terjadi. Tidak tepat untuk melemparkan pengecualian di sini; pemanggil diperlukan untuk menangani situasi di mana prasyarat gagal, yang tidak menyelesaikan masalah mendasar.
Metode implementasi pemblokiran 2: melalui polling dan tidur
Copy kode kodenya sebagai berikut:
public void put2(V v) melempar InterruptedException {
while (benar) {//Polling
disinkronkan (ini) {
jika (! isFull()) {
lakukanPut(v);
kembali;
}
}
Thread.sleep(SLEEP_TIME);//Tidur
}
}
Analisis: Sulit untuk menimbang pengaturan waktu tidur SLEEP_TIME. Jika pengaturannya terlalu kecil, CPU mungkin melakukan polling beberapa kali, menghabiskan lebih banyak sumber daya CPU; jika pengaturannya terlalu besar, responsnya akan lebih rendah.
Memblokir metode implementasi tiga: antrian bersyarat
Elemen dalam antrian kondisi adalah thread yang menunggu kondisi terkait satu per satu. Setiap objek Java dapat digunakan sebagai kunci, dan setiap objek juga dapat digunakan sebagai antrian kondisi, dan metode wait, notify, dan notifyAll di Object merupakan API dari antrian kondisi internal. Object.wait akan secara otomatis melepaskan kunci dan meminta sistem operasi untuk menangguhkan thread saat ini sehingga thread lain dapat memperoleh kunci dan mengubah status objek. Object.notify dan Object.notifyAll dapat membangunkan thread yang menunggu, memilih thread dari antrian kondisi untuk membangunkannya dan mencoba mendapatkan kembali kuncinya.
Copy kode kodenya sebagai berikut:
void put3(V v) yang disinkronkan secara publik melempar InterruptedException {
sementara(sudah Penuh())
Tunggu();
doput(v);
beri tahuSemua();
}
Analisis: Dapatkan respon yang lebih baik, sederhana dan mudah digunakan.
Gunakan antrian bersyarat
1. Predikat bersyarat
1).Definisi: Predikat bersyarat adalah prasyarat agar suatu operasi menjadi operasi yang bergantung pada keadaan. Predikat bersyarat adalah ekspresi yang terdiri dari variabel keadaan individual di kelas. Misalnya, predikat kondisional untuk metode put adalah "cache tidak kosong".
2).Hubungan: Ada hubungan terner yang penting dalam penantian bersyarat, termasuk penguncian, metode tunggu, dan predikat bersyarat. Beberapa variabel keadaan termasuk dalam predikat kondisional, dan setiap variabel keadaan harus dilindungi oleh kunci, sehingga kunci tersebut harus ditahan sebelum menguji predikat kondisional. Objek lock dan objek antrian bersyarat (dan objek tempat metode wait dan notify dipanggil) harus merupakan objek yang sama.
3). Batasan: Setiap panggilan untuk menunggu akan secara implisit dikaitkan dengan predikat kondisi tertentu. Ketika predikat kondisi tertentu dipanggil, pemanggil harus sudah memegang kunci yang terkait dengan antrian kondisi. variabel keadaan
2. Aturan penggunaan antrian bersyarat
1). Biasanya ada predikat bersyarat
2).Selalu uji predikat bersyarat sebelum memanggil tunggu, dan uji lagi setelah kembali dari menunggu;
3).Selalu panggil wait dalam loop;
4) Memastikan bahwa variabel keadaan yang merupakan predikat kondisi dilindungi oleh kunci, dan kunci ini harus dikaitkan dengan antrian kondisi;
5). Saat memanggil wait, notify dan notifyAll, kunci yang terkait dengan antrian kondisi harus ditahan;
6). Setelah memeriksa predikat bersyarat, jangan melepaskan kunci sebelum mulai menjalankan logika yang dilindungi;
3.Pemberitahuan
Cobalah untuk menggunakan notifyAll daripada nofify. Karena nofify akan secara acak membangunkan thread dari keadaan tidak aktif ke keadaan Diblokir (Keadaan yang diblokir adalah thread yang selalu mencoba untuk mendapatkan kunci, yaitu, setelah menemukan bahwa kunci tersebut tersedia, maka thread tersebut akan aktif. akan segera menahan kunci), sementara notifyAll Ini akan membangunkan semua thread dalam antrian kondisi dari keadaan tidak aktif ke keadaan Diblokir. Pertimbangkan situasi ini, jika thread A memasuki keadaan tidak aktif karena predikat kondisi Pa, dan thread B memasuki keadaan dorman karena kondisi predikat Pb. Saat ini Pb benar, thread C mengeksekusi satu notifikasi. Jika JVM secara acak memilih thread A untuk dibangunkan, maka thread A memeriksa apakah predikat kondisional Pa tidak benar dan kemudian memasuki kondisi tidur , dan program akan selalu dalam kondisi tidur jika Anda menggunakannya notifyAll berbeda. JVM akan membangunkan semua thread yang menunggu dalam antrian kondisi dari kondisi tidur ke kondisi Diblokir. Bahkan jika thread dipilih secara acak dan memasuki kondisi tidur karena predikat kondisi tidak benar, thread lain akan bersaing untuk mendapatkan kunci dan. lanjutkan eksekusi.
4. Kode salinan formulir standar metode ketergantungan negara adalah sebagai berikut:
void stateDependentMethod throwsInterruptedException{
disinkronkan(kunci){
while(!kondisiPredikat))
lock.tunggu();
}
//melakukan sesuatu();
....
beri tahuSemua();
}
Tampilkan objek Kondisi
Objek Kondisi yang eksplisit adalah alternatif yang lebih fleksibel dan menyediakan fungsionalitas yang lebih kaya: beberapa waktu tunggu dapat terjadi pada setiap kunci, waktu tunggu yang bersyarat dapat diinterupsi atau tidak dapat diinterupsi, menunggu berdasarkan waktu, dan operasi antrean yang adil atau tidak adil. Suatu Kondisi dapat dikaitkan dengan Kunci, sama seperti antrian kondisi yang dikaitkan dengan kunci bawaan. Untuk membuat Kondisi, panggil metode Lock.newCondition pada Kunci terkait. Kode berikut digunakan untuk mengimplementasikan kembali cache yang dibatasi menggunakan variabel kondisi tampilan. Kode tersebut adalah sebagai berikut:
kelas publik ConditionBoundedBuffer<V> {
penggemar V[] akhir pribadi;
ekor int pribadi;
kepala int pribadi;
jumlah int pribadi;
kunci kunci pribadi = new ReentrantLock();
Kondisi pribadi notFullCondition = lock.newCondition();
Kondisi pribadi notEmptyCondition = lock.newCondition();
@SuppressWarnings("tidak dicentang")
public ConditionBoundedBuffer(int kapasitas) {
buf = (V[]) Objek baru[kapasitas];
}
public void doPut(V v) melempar InterruptedException {
mencoba {
kunci.kunci();
while (hitungan == buf.panjang)
notFullCondition.menunggu();
buf[ekor] = v;
if (++tail == buf.panjang)
ekor = 0;
hitungan++;
notEmptyCondition.sinyal();
} Akhirnya {
kunci.buka kunci();
}
}
publik V doTake() melempar InterruptedException {
mencoba {
kunci.kunci();
sementara (hitung == 0)
notEmptyCondition.menunggu();
V v = buf[kepala];
buf[kepala] = nol;
if (++head == buf.panjang)
kepala = 0;
menghitung--;
notFullCondition.sinyal();
kembali v;
} Akhirnya {
kunci.buka kunci();
}
}
}