Baru -baru ini, saya menemukan beberapa skenario konkurensi tinggi di tempat kerja yang membutuhkan penguncian untuk memastikan kebenaran logika bisnis, dan kinerja setelah penguncian tidak boleh terlalu terpengaruh. Gagasan awalnya adalah mengunci data melalui kata kunci seperti cap waktu dan ID, sehingga memastikan konkurensi berbagai jenis pemrosesan data. Granularitas kunci yang disediakan oleh API Java sendiri terlalu besar, dan sulit untuk memenuhi kebutuhan ini pada saat yang sama, jadi saya menulis beberapa ekstensi sederhana sendiri ...
1. Kunci Segmen
Menggambar pada gagasan segmentasi concurrenthashmap, sejumlah kunci dibuat, dan ketika menggunakannya, kunci yang sesuai dikembalikan sesuai dengan kunci. Ini adalah strategi kunci yang paling sederhana dan tertinggi dan akhirnya diadopsi di antara beberapa implementasi. Kodenya adalah sebagai berikut:
/*** Kunci segmen, sistem menyediakan sejumlah kunci asli, dapatkan kunci yang sesuai berdasarkan nilai hash dari objek yang masuk dan menambahkan kunci* Catatan: Jika nilai hash dari objek yang dikunci, dapat menyebabkan kunci tidak dapat dirilis dengan sukses !!! */SegmentLock kelas publik <T> {private integer segmen = 16; // Jumlah default segmen hashmap final pribadi <integer, reentrantlock> lockmap = hashmap baru <> (); SegmentLock publik () {init (null, false); } public SegmentLock (Integer Counts, Boolean Fair) {init (Counts, Fair); } private void init (integer counts, boolean fair) {if (counts! = null) {segments = counts; } untuk (int i = 0; i <segmen; i ++) {lockmap.put (i, reentrantlock baru (wajar)); }} public void lock (Key t) {reentrantlock lock = lockMap.get ((key.hashCode () >>> 1) % segmen); lock.lock (); } public void unlock (t key) {reentrantlock lock = lockmap.get ((key.hashCode () >>> 1) % segmen); lock.unlock (); }}2. Hash Lock
Strategi kunci kedua yang dikembangkan berdasarkan kunci tersegmentasi di atas adalah untuk mencapai kunci berbutir halus yang sebenarnya. Setiap objek dengan nilai hash yang berbeda dapat memperoleh kunci independennya sendiri. Dalam pengujian, ketika kode yang terkunci dieksekusi dengan cepat, efisiensinya sekitar 30% lebih lambat dari kunci segmen. Jika ada operasi yang memakan waktu yang lama, perasaan kinerja harus lebih baik. Kodenya adalah sebagai berikut:
hashlock kelas publik <t> {private boolean isfair = false; SegmentLock akhir pribadi <T> SegmentLock = SegmentLock baru <> (); // Segmen Kunci Privat ConcurrentHashMap <t, lockinfo> lockMap = concurrenthashMap baru <> (); hashlock publik () {} hashlock publik (boolean fair) {isFair = fair; } public void lock (tombol t) {lockinfo lockinfo; segmentlock.lock (kunci); coba {lockInfo = lockMap.get (key); if (lockInfo == null) {lockinfo = new LockInfo (isFair); lockmap.put (kunci, lockinfo); } else {lockinfo.count.incrementandget (); }} akhirnya {SegmentLock.unlock (key); } lockinfo.lock.lock (); } public void unlock (tombol t) {lockinfo lockinfo = lockMap.get (key); if (lockinfo.count.get () == 1) {segmentLock.lock (key); coba {if (lockinfo.count.get () == 1) {lockmap.remove (key); }} akhirnya {SegmentLock.unlock (key); }} lockinfo.count.decrementandget (); lockinfo.unlock (); } private static class lockInfo {public reentrantlock lock; jumlah atomicinteger publik = atomicinteger baru (1); private lockinfo (boolean fair) {this.lock = baru reentrantlock (fair); } public void lock () {this.lock.lock (); } public void unlock () {this.lock.unlock (); }}}3. Kunci Referensi Lemah
Kunci hash selalu cacat karena kunci tersegmentasi diperkenalkan untuk memastikan sinkronisasi pembuatan dan kehancuran kunci, sehingga kunci ketiga ditulis untuk mencari kinerja yang lebih baik dan kunci berbutir lebih halus. Gagasan kunci ini adalah untuk membuat kunci dengan bantuan referensi yang lemah di Java, dan menyerahkan kehancuran kunci ke koleksi sampah JVM untuk menghindari konsumsi tambahan.
Agak disesalkan bahwa karena concurrenthashmap digunakan sebagai wadah kunci, tidak dapat benar -benar menghilangkan kunci tersegmentasi. Kinerja kunci ini sekitar 10% lebih cepat dari hashlock. Kode kunci:
/*** Kunci referensi yang lemah, menyediakan fungsi penguncian independen untuk setiap hash independen*/kelas publik Lemari HASHLOCK <T> {private concurrenthashMap <t, weaklockref <t, reentrantlock>> lockmap = concurrenthashmap baru <> (); Private ReferenceQueue <Reentrantlock> antrian = ReferenceQueue baru <> (); Publik reentrantlock get (tombol t) {if (lockMap.size ()> 1000) {clearemptyref (); } Lemah Reference <Reentrantlock> lockref = lockmap.get (key); Reentrantlock lock = (lockref == null? Null: lockref.get ()); while (lock == null) {lockMap.putIfabSent (key, New WeakLockRef <> (baru reentrantlock (), antrian, kunci)); lockref = lockmap.get (key); lock = (lockref == null? null: lockref.get ()); if (lock! = null) {return lock; } clearemptyref (); } return lock; } @Suppresswarnings ("Uncecked") Private void clearemptyref () {referensi <? memperluas reentrantlock> ref; while ((ref = queue.poll ())! = null) {weaklockref <t ,? memperluas reentrantlock> weaklockref = (weaklockref <t ,? extends reentrantlock>) ref; lockmap.remove (weaklockref.key); }} Private Static Final Class Final TrownLockRef <T, K> memperluas Lemah Referensi <K> {Kunci T akhir; private weastlockref (k referensi, referensiqueue <? super k> q, t key) {super (referensi, q); this.key = key; }}}nota bene
Pada awalnya saya ingin menggunakan Locksupport dan AQS untuk mengimplementasikan kunci berbutir halus. Seperti yang saya tulis, saya menemukan bahwa hal -hal yang saya implementasikan tidak jauh berbeda dari kunci Java asli, jadi saya menyerahkan enkapsulasi kunci Java sendiri, yang membuang banyak waktu.
Bahkan, setelah menerapkan kunci berbutir halus ini, kami memiliki ide-ide baru, seperti mengirimkan data ke utas khusus melalui ide-ide segmentasi, yang dapat mengurangi waktu pemblokiran sejumlah besar utas dan menyerahkannya pada eksplorasi di masa depan ...