Récemment, j'ai rencontré des scénarios de concurrence élevés au travail qui nécessitent le verrouillage pour garantir l'exactitude de la logique commerciale, et les performances après verrouillage ne doivent pas être trop affectées. L'idée initiale est de verrouiller les données via des mots clés tels que les horodatages et les ID, garantissant ainsi la concurrence des différents types de traitement des données. La granularité de verrouillage fournie par la propre API de Java est trop grande, et il est difficile de répondre à ces besoins en même temps, alors j'ai moi-même écrit plusieurs extensions simples ...
1. Verrouillage du segment
S'appuyant sur l'idée de segmentation de ConcurrentHashMap, un certain nombre de verrous sont créés et lors de l'utilisation, le verrou correspondant est renvoyé en fonction de la clé. Il s'agit de la stratégie de verrouillage la plus simple et la plus élevée et finalement adoptée parmi plusieurs implémentations. Le code est le suivant:
/ ** * Lock du segment, le système fournit un certain nombre de verrous d'origine, obtenez le verrou correspondant en fonction de la valeur de hachage de l'objet entrant et ajoutez le verrou * Remarque: Si la valeur de hachage de l'objet est verrouillé, il peut entraîner la libération de la serrure avec succès !!! * / public class segmentlock <T> {private Integer segments = 16; // Nombre de segments par défaut de segments privé hashmap final <Integer, reentrantLock> LockMap = new hashmap <> (); public segmentlock () {init (null, false); } public segmentlock (comptes entiers, foire booléenne) {init (coutes, foire); } private void init (entiers comptes, booléen fair) {if (counts! = null) {segments = counts; } pour (int i = 0; i <segments; i ++) {lockmap.put (i, new reentrantLock (Fair)); }} public void Lock (key) {reentrantLock lock = lockmap.get ((key.hashcode () >>> 1)% segments); lock.lock (); } public void unlock (t key) {reentrantLock lock = lockmap.get ((key.hashcode () >>> 1)% segments); lock.unlock (); }}2. Lock de hachage
La deuxième stratégie de verrouillage développée sur la base du verrouillage segmenté ci-dessus est d'obtenir un véritable verrou à grain fin. Chaque objet avec une valeur de hachage différente peut obtenir son propre verrou indépendant. Dans le test, lorsque le code verrouillé est exécuté rapidement, l'efficacité est environ 30% plus lente que le verrouillage du segment. S'il y a une longue opération de longue date, le sentiment de performance devrait être meilleur. Le code est le suivant:
classe publique Hashlock <T> {private boolean isfair = false; SEGLAGE FINAL privé <T> segmentlock = new segmentlock <> (); // Lock segment private final concurrenthashmap <t, lockinfo> lockmap = new concurrenthashmap <> (); Public Hashlock () {} public Hashlock (Boolean Fair) {isfair = Fair; } public void Lock (touche t) {Lockinfo Lockinfo; segmentlock.lock (clé); essayez {lockinfo = lockmap.get (key); if (lockinfo == null) {lockinfo = new lockinfo (isfair); LockMap.put (Key, Lockinfo); } else {lockinfo.count.incrementAndget (); }} enfin {segmentlock.unlock (key); } lockinfo.lock.lock (); } public void unlock (k key) {lockinfo lockinfo = lockmap.get (key); if (lockinfo.count.get () == 1) {segmentlock.lock (key); try {if (lockinfo.count.get () == 1) {LockMap.Remove (key); }} enfin {segmentlock.unlock (key); }} lockinfo.count.decmentAndget (); lockinfo.unlock (); } classe statique privée Lockinfo {Public Reentrantlock Lock; Count public atomicInteger = New AtomicInteger (1); Lockinfo privé (booléen foire) {this.lock = new reentrantLock (foire); } public void Lock () {this.lock.lock (); } public void unlock () {this.lock.unlock (); }}}3. Verrouillage de référence faible
Les serrures de hachage sont toujours erronées car les serrures segmentées sont introduites pour assurer la synchronisation de la création et de la destruction des verrous, donc une troisième serrure a été écrite pour rechercher de meilleures performances et des serrures à grain plus fines. L'idée de cette serrure est de créer une serrure à l'aide de références faibles en Java et de remettre la destruction de la serrure à la collecte des ordures de JVM pour éviter une consommation supplémentaire.
Il est un peu regrettable que, parce que concurrenthashmap est utilisé comme conteneur de verrouillage, il est incapable de vraiment se débarrasser des serrures segmentées. Les performances de cette serrure sont environ 10% plus rapides que celles de Hashlock. Code de verrouillage:
/ ** * Lock de référence faible, fournissant une fonction de verrouillage indépendante pour chaque hachage indépendant * / classe publique faiblehashlock <T> {private concurrenthashmap <t, faiblelockref <t, reentrantlock>> LockMap = new concurrenthashmap <> (); Private ReferenceQueue <ReentrantLock> file d'attente = new ReferenceQueue <> (); public reentrantlock get (t key) {if (lockmap.size ()> 1000) {ClearEmpTyRef (); } FaibleReference <reentrantLock> LockRef = LockMap.get (key); ReentrantLock Lock = (LockRef == NULL? NULL: LockRef.get ()); while (lock == null) {lockmap.putifabsent (key, new faibleLockref <> (new reentrantLock (), file d'attente, key)); lockref = lockmap.get (key); Lock = (LockRef == NULL? NULL: LockRef.get ()); if (Lock! = null) {return Lock; } ClearEmpTyRef (); } return verrouillage; } @SuppressWarnings ("Unchecked") private void clearEmpTyRef () {référence <? étend reentrantlock> réf; while ((ref = queue.poll ())! = null) {faiblelockref <t ,? étend reentrantLock> faiblelockref = (faiblelockref <t ,? étend reentrantlock>) ref; LockMap.Remove (faiblelockref.key); }} classe finale statique privée FaiblelockRef <t, k> étend la touche faible <k> {la touche finale T; Private faiblelockref (k référence, Referenceeue <? Super K> Q, T key) {super (référent, q); this.key = key; }}}post-scriptum
Au début, je voulais utiliser LockSupport et AQS pour implémenter des verrous à grains fins. Comme je l'ai écrit, j'ai constaté que les choses que je mettais en œuvre n'étaient pas très différentes de ces serrures natives Java, alors j'ai abandonné l'encapsulation des propres serrures de Java, ce qui a perdu beaucoup de temps.
En fait, après la mise en œuvre de ces serrures à grain fin, nous avons de nouvelles idées, telles que la soumission de données à des fils spéciaux grâce à des idées de segmentation, ce qui peut réduire le temps de blocage d'un grand nombre de fils et laisser à l'exploration future ...