Maison > Java > javaDidacticiel > le corps du texte

Exemple de code détaillé de trois façons d'implémenter des verrous à granularité fine en Java

黄舟
Libérer: 2017-03-30 10:48:28
original
1258 Les gens l'ont consulté

Récemment, j'ai rencontré au travail des scénarios à haute concurrence qui nécessitent un verrouillage pour garantir l'exactitude de la logique métier, et il est nécessaire que les performances ne soient pas grandement affectées après le verrouillage. L'idée initiale est de verrouiller l' horodatage , l'identifiant et d'autres mots-clés des données pour garantir la simultanéité des différents types de traitement de 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, j'ai donc écrit moi-même quelques extensions simples...

1. Verrou segmenté

S'inspirez de l'idée de segmentation de concurrentHashMap, générez d'abord un certain nombre de verrous, puis renvoyez le verrou correspondant en fonction de la clé lorsqu'il est utilisé. Il s'agit de la stratégie de verrouillage la plus simple et la plus performante parmi plusieurs implémentations, et c'est également la stratégie de verrouillage qui a finalement été adoptée. Le code est le suivant :

/**
 * 分段锁,系统提供一定数量的原始锁,根据传入对象的哈希值获取对应的锁并加锁
 * 注意:要锁的对象的哈希值如果发生改变,有可能导致锁无法成功释放!!!
 */
public class SegmentLock<T> {
    private Integer segments = 16;//默认分段数量
    private final HashMap<Integer, ReentrantLock> lockMap = new HashMap<>();

    public SegmentLock() {
        init(null, false);
    }

    public SegmentLock(Integer counts, boolean fair) {
        init(counts, fair);
    }

    private void init(Integer counts, boolean fair) {
        if (counts != null) {
            segments = counts;
        }
        for (int i = 0; i < segments; i++) {
            lockMap.put(i, new ReentrantLock(fair));
        }
    }

    public void lock(T key) {
        ReentrantLock lock = lockMap.get(key.hashCode() % segments);
        lock.lock();
    }

    public void unlock(T key) {
        ReentrantLock lock = lockMap.get(key.hashCode() % segments);
        lock.unlock();
    }
}
Copier après la connexion

2. verrouillage segmenté ci-dessus La deuxième stratégie de verrouillage développée consiste à obtenir un véritable verrouillage à granularité fine. Chaque objet avec une valeur de hachage différente peut obtenir son propre verrou indépendant. Lors des tests, lorsque le code verrouillé s'exécute très rapidement, l'efficacité est environ 30 % plus lente que le verrouillage segmenté. S'il y a des opérations à long terme, il semble que les performances devraient être meilleures. Le code est le suivant :

3. Faible
public class HashLock<T> {
    private boolean isFair = false;
    private final SegmentLock<T> segmentLock = new SegmentLock<>();//分段锁
    private final ConcurrentHashMap<T, LockInfo> lockMap = new ConcurrentHashMap<>();

    public HashLock() {
    }

    public HashLock(boolean fair) {
        isFair = fair;
    }

    public void lock(T key) {
        LockInfo lockInfo;
        segmentLock.lock(key);
        try {
            lockInfo = lockMap.get(key);
            if (lockInfo == null) {
                lockInfo = new LockInfo(isFair);
                lockMap.put(key, lockInfo);
            } else {
                lockInfo.count.incrementAndGet();
            }
        } finally {
            segmentLock.unlock(key);
        }
        lockInfo.lock.lock();
    }

    public void unlock(T key) {
        LockInfo lockInfo = lockMap.get(key);
        if (lockInfo.count.get() == 1) {
            segmentLock.lock(key);
            try {
                if (lockInfo.count.get() == 1) {
                    lockMap.remove(key);
                }
            } finally {
                segmentLock.unlock(key);
            }
        }
        lockInfo.count.decrementAndGet();
        lockInfo.unlock();
    }

    private static class LockInfo {
        public ReentrantLock lock;
        public AtomicInteger count = new AtomicInteger(1);

        private LockInfo(boolean fair) {
            this.lock = new ReentrantLock(fair);
        }

        public void lock() {
            this.lock.lock();
        }

        public void unlock() {
            this.lock.unlock();
        }
    }
}
Copier après la connexion
référence

verrou verrou de hachage car le verrou segmenté est introduit pour assurer la synchronisation de la création et de la destruction du verrou, cela semble toujours un peu défectueux, j'ai donc écrit un troisième verrou pour rechercher de meilleures performances et des verrous plus fins. L'idée de ce verrou est d'utiliser la référence faible de Java pour créer le verrou et de confier la destruction du verrou au garbage collection de la JVM pour éviter une consommation supplémentaire.

Il est dommage que, comme ConcurrentHashMap est utilisé comme conteneur de verrouillage, il ne puisse pas vraiment se débarrasser du verrou de segmentation. Les performances de ce verrou sont environ 10 % plus rapides que celles de HashLock. Code de verrouillage :

Postscript
/**
 * 弱引用锁,为每个独立的哈希值提供独立的锁功能
 */
public class WeakHashLock<T> {
    private ConcurrentHashMap<T, WeakLockRef<T, ReentrantLock>> lockMap = new ConcurrentHashMap<>();
    private ReferenceQueue<ReentrantLock> queue = new ReferenceQueue<>();

    public ReentrantLock get(T key) {
        if (lockMap.size() > 1000) {
            clearEmptyRef();
        }
        WeakReference<ReentrantLock> lockRef = lockMap.get(key);
        ReentrantLock lock = (lockRef == null ? null : lockRef.get());
        while (lock == null) {
            lockMap.putIfAbsent(key, new WeakLockRef<>(new ReentrantLock(), queue, key));
            lockRef = lockMap.get(key);
            lock = (lockRef == null ? null : lockRef.get());
            if (lock != null) {
                return lock;
            }
            clearEmptyRef();
        }
        return lock;
    }

    @SuppressWarnings("unchecked")
    private void clearEmptyRef() {
        Reference<? extends ReentrantLock> ref;
        while ((ref = queue.poll()) != null) {
            WeakLockRef<T, ? extends ReentrantLock> weakLockRef = (WeakLockRef<T, ? extends ReentrantLock>) ref;
            lockMap.remove(weakLockRef.key);
        }
    }

    private static final class WeakLockRef<T, K> extends WeakReference<K> {
        final T key;

        private WeakLockRef(K referent, ReferenceQueue<? super K> q, T key) {
            super(referent, q);
            this.key = key;
        }
    }
}
Copier après la connexion

Au début, je voulais utiliser locksupport et AQS pour implémenter des verrous fins. Au fur et à mesure que je l'écrivais, j'ai trouvé que ce qui était implémenté n'était pas grand-chose. différent des verrous natifs de Java J'ai donc abandonné et j'ai opté pour l'encapsulation du propre verrou de Java, ce qui m'a fait perdre beaucoup de temps.

En fait, après la mise en œuvre de ces verrous à granularité fine, de nouvelles idées ont émergé. Par exemple, les données peuvent être soumises à des threads spécialisés pour être traitées par segmentation, ce qui peut réduire le temps de blocage d'un grand nombre de threads et. laissez-le au futur Explorez…

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal