Heim  >  Artikel  >  Java  >  Detaillierter Beispielcode für drei Möglichkeiten zur Implementierung feinkörniger Sperren in Java

Detaillierter Beispielcode für drei Möglichkeiten zur Implementierung feinkörniger Sperren in Java

黄舟
黄舟Original
2017-03-30 10:48:281241Durchsuche

Vor kurzem bin ich bei der Arbeit auf einige Szenarien mit hoher Parallelität gestoßen, die eine Sperre erfordern, um die Korrektheit der Geschäftslogik sicherzustellen, und es ist erforderlich, dass die Leistung nach der Sperrung nicht stark beeinträchtigt wird. Die ursprüngliche Idee besteht darin, den Zeitstempel , die ID und andere Schlüsselwörter der Daten zu sperren, um die Parallelität verschiedener Arten der Datenverarbeitung sicherzustellen. Die von Javas eigener api bereitgestellte Sperrgranularität ist zu groß und es ist schwierig, diese Anforderungen gleichzeitig zu erfüllen, daher habe ich selbst ein paar einfache Erweiterungen geschrieben ...

1. Segmentierte Sperre

Nutzen Sie die Segmentierungsidee von concurrentHashMap, generieren Sie zunächst eine bestimmte Anzahl von Sperren und geben Sie dann bei Verwendung die entsprechende Sperre basierend auf dem Schlüssel zurück. Dies ist die einfachste und leistungsfähigste unter mehreren Implementierungen und ist auch die Sperrstrategie, die schließlich übernommen wurde. Der Code lautet wie folgt:

/**
 * 分段锁,系统提供一定数量的原始锁,根据传入对象的哈希值获取对应的锁并加锁
 * 注意:要锁的对象的哈希值如果发生改变,有可能导致锁无法成功释放!!!
 */
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();
    }
}

2 Hash-Sperre

Basierend auf oben segmentierte Sperre Die zweite entwickelte Sperrstrategie besteht darin, eine echte feinkörnige Sperre zu erreichen. Jedes Objekt mit einem anderen Hashwert kann eine eigene unabhängige Sperre erhalten. In Tests war die Effizienz bei der sehr schnellen Ausführung des gesperrten Codes etwa 30 % langsamer als bei der segmentierten Sperrung. Bei Langzeitoperationen sollte die Leistung gefühlt besser sein. Der Code lautet wie folgt:

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();
        }
    }
}

3. Schwache Referenz- Sperre

Hash-Sperre, da die segmentierte Sperre eingeführt wird, um die Synchronisierung der Sperrenerstellung und -zerstörung sicherzustellen. Es fühlt sich immer etwas fehlerhaft an, deshalb habe ich eine dritte Sperre geschrieben, um eine bessere Leistung und feinkörnigere Sperren zu erzielen. Die Idee dieser Sperre besteht darin, die schwache Referenz von Java zum Erstellen der Sperre zu verwenden und die Zerstörung der Sperre an die Garbage Collection der JVM zu übergeben, um zusätzlichen Verbrauch zu vermeiden.

Es ist schade, dass ConcurrentHashMap als Sperrcontainer verwendet wird und die Segmentierungssperre nicht wirklich entfernt werden kann. Die Leistung dieser Sperre ist etwa 10 % schneller als die von HashLock. Sperrcode:

/**
 * 弱引用锁,为每个独立的哈希值提供独立的锁功能
 */
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;
        }
    }
}

Postscript

Zuerst wollte ich Locksupport und AQS verwenden, um feinkörnige Sperren zu implementieren. Während ich schrieb, stellte ich fest, dass das, was implementiert wurde, nicht viel anders war Von Javas nativen Sperren habe ich also aufgegeben und bin dazu übergegangen, Javas eigene Sperre zu kapseln, was viel Zeit verschwendet hat.

Tatsächlich sind nach der Implementierung dieser feinkörnigen Sperren neue Ideen entstanden. Beispielsweise können Daten zur Verarbeitung durch Segmentierungsdenken übermittelt werden, wodurch die Blockierungszeit einer großen Anzahl von Threads verkürzt werden kann und überlassen Sie es der Zukunft.

Das obige ist der detaillierte Inhalt vonDetaillierter Beispielcode für drei Möglichkeiten zur Implementierung feinkörniger Sperren in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn