热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java原生锁机制:多种细粒度锁实现方法深入解析

在处理高并发场景时,确保业务逻辑的正确性是关键。本文深入探讨了Java原生锁机制的多种细粒度实现方法,旨在通过使用数据的时间戳、ID等关键字段进行锁定,以最小化对系统性能的影响。文章详细分析了不同锁策略的优缺点,并提供了实际应用中的最佳实践,帮助开发者在高并发环境下高效地实现锁机制。

最近在工作上碰见了一些高并发的场景需要加锁来保证业务逻辑的正确性,并且要求加锁后性能不能受到太大的影响。初步的想法是通过数据的时间戳,id等关键字来加锁,从而保证不同类型数据处理的并发性。而java自身api提供的锁粒度太大,很难同时满足这些需求,于是自己动手写了几个简单的扩展...

1. 分段锁

借鉴concurrentHashMap的分段思想,先生成一定数量的锁,具体使用的时候再根据key来返回对应的lock。这是几个实现里最简单,性能最高,也是最终被采用的锁策略,代码如下:

/**

* 分段锁,系统提供一定数量的原始锁,根据传入对象的哈希值获取对应的锁并加锁

* 注意:要锁的对象的哈希值如果发生改变,有可能导致锁无法成功释放!!!

*/

public class SegmentLock {

private Integer segments = 16;//默认分段数量

private final HashMap lockMap &#61; 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 !&#61; null) {

segments &#61; counts;

}

for (int i &#61; 0; i

lockMap.put(i, new ReentrantLock(fair));

}

}

public void lock(T key) {

ReentrantLock lock &#61; lockMap.get((key.hashCode()>>>1) % segments);

lock.lock();

}

public void unlock(T key) {

ReentrantLock lock &#61; lockMap.get((key.hashCode()>>>1) % segments);

lock.unlock();

}

}

2. 哈希锁

上述分段锁的基础上发展起来的第二种锁策略&#xff0c;目的是实现真正意义上的细粒度锁。每个哈希值不同的对象都能获得自己独立的锁。在测试中&#xff0c;在被锁住的代码执行速度飞快的情况下&#xff0c;效率比分段锁慢 30% 左右。如果有长耗时操作&#xff0c;感觉表现应该会更好。代码如下&#xff1a;

public class HashLock {

private boolean isFair &#61; false;

private final SegmentLock segmentLock &#61; new SegmentLock<>();//分段锁

private final ConcurrentHashMap lockMap &#61; new ConcurrentHashMap<>();

public HashLock() {

}

public HashLock(boolean fair) {

isFair &#61; fair;

}

public void lock(T key) {

LockInfo lockInfo;

segmentLock.lock(key);

try {

lockInfo &#61; lockMap.get(key);

if (lockInfo &#61;&#61; null) {

lockInfo &#61; 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 &#61; lockMap.get(key);

if (lockInfo.count.get() &#61;&#61; 1) {

segmentLock.lock(key);

try {

if (lockInfo.count.get() &#61;&#61; 1) {

lockMap.remove(key);

}

} finally {

segmentLock.unlock(key);

}

}

lockInfo.count.decrementAndGet();

lockInfo.unlock();

}

private static class LockInfo {

public ReentrantLock lock;

public AtomicInteger count &#61; new AtomicInteger(1);

private LockInfo(boolean fair) {

this.lock &#61; new ReentrantLock(fair);

}

public void lock() {

this.lock.lock();

}

public void unlock() {

this.lock.unlock();

}

}

}

3. 弱引用锁

哈希锁因为引入的分段锁来保证锁创建和销毁的同步&#xff0c;总感觉有点瑕疵&#xff0c;所以写了第三个锁来寻求更好的性能和更细粒度的锁。这个锁的思想是借助java的弱引用来创建锁&#xff0c;把锁的销毁交给jvm的垃圾回收&#xff0c;来避免额外的消耗。

有点遗憾的是因为使用了ConcurrentHashMap作为锁的容器&#xff0c;所以没能真正意义上的摆脱分段锁。这个锁的性能比 HashLock 快10% 左右。锁代码&#xff1a;

/**

* 弱引用锁&#xff0c;为每个独立的哈希值提供独立的锁功能

*/

public class WeakHashLock {

private ConcurrentHashMap> lockMap &#61; new ConcurrentHashMap<>();

private ReferenceQueue queue &#61; new ReferenceQueue<>();

public ReentrantLock get(T key) {

if (lockMap.size() > 1000) {

clearEmptyRef();

}

WeakReference lockRef &#61; lockMap.get(key);

ReentrantLock lock &#61; (lockRef &#61;&#61; null ? null : lockRef.get());

while (lock &#61;&#61; null) {

lockMap.putIfAbsent(key, new WeakLockRef<>(new ReentrantLock(), queue, key));

lockRef &#61; lockMap.get(key);

lock &#61; (lockRef &#61;&#61; null ? null : lockRef.get());

if (lock !&#61; null) {

return lock;

}

clearEmptyRef();

}

return lock;

}

&#64;SuppressWarnings("unchecked")

private void clearEmptyRef() {

Reference extends ReentrantLock> ref;

while ((ref &#61; queue.poll()) !&#61; null) {

WeakLockRef weakLockRef &#61; (WeakLockRef) ref;

lockMap.remove(weakLockRef.key);

}

}

private static final class WeakLockRef extends WeakReference {

final T key;

private WeakLockRef(K referent, ReferenceQueue super K> q, T key) {

super(referent, q);

this.key &#61; key;

}

}

}

后记

最开始想借助 locksupport 和 AQS 来实现细粒度锁&#xff0c;写着写着发现正在实现的东西和java 原生的锁区别不大&#xff0c;于是放弃改为对java自带锁的封装&#xff0c;浪费了不少时间。

实际上在实现了这些细粒度锁之后&#xff0c;又有了新的想法&#xff0c;比如可以通过分段思想将数据提交给专门的线程来处理&#xff0c;可以减少大量线程的阻塞时间&#xff0c;留待日后探索...



推荐阅读
  • Java 8 引入了 Stream API,这一新特性极大地增强了集合数据的处理能力。通过 Stream API,开发者可以更加高效、简洁地进行集合数据的遍历、过滤和转换操作。本文将详细解析 Stream API 的核心概念和常见用法,帮助读者更好地理解和应用这一强大的工具。 ... [详细]
  • Ceph API微服务实现RBD块设备的高效创建与安全删除
    本文旨在实现Ceph块存储中RBD块设备的高效创建与安全删除功能。开发环境为CentOS 7,使用 IntelliJ IDEA 进行开发。首先介绍了 librbd 的基本概念及其在 Ceph 中的作用,随后详细描述了项目 Gradle 配置的优化过程,确保了开发环境的稳定性和兼容性。通过这一系列步骤,我们成功实现了 RBD 块设备的快速创建与安全删除,提升了系统的整体性能和可靠性。 ... [详细]
  • 本文提供了 RabbitMQ 3.7 的快速上手指南,详细介绍了环境搭建、生产者和消费者的配置与使用。通过官方教程的指引,读者可以轻松完成初步测试和实践,快速掌握 RabbitMQ 的核心功能和基本操作。 ... [详细]
  • 在启用分层编译的情况下,即时编译器(JIT)的触发条件涉及多个因素,包括方法调用频率、代码复杂度和运行时性能数据。本文将详细解析这些条件,并探讨分层编译如何优化JVM的执行效率。 ... [详细]
  • 本题库精选了Java核心知识点的练习题,旨在帮助学习者巩固和检验对Java理论基础的掌握。其中,选择题部分涵盖了访问控制权限等关键概念,例如,Java语言中仅允许子类或同一包内的类访问的访问权限为protected。此外,题库还包括其他重要知识点,如异常处理、多线程、集合框架等,全面覆盖Java编程的核心内容。 ... [详细]
  • 在稀疏直接法视觉里程计中,通过优化特征点并采用基于光度误差最小化的灰度图像线性插值技术,提高了定位精度。该方法通过对空间点的非齐次和齐次表示进行处理,利用RGB-D传感器获取的3D坐标信息,在两帧图像之间实现精确匹配,有效减少了光度误差,提升了系统的鲁棒性和稳定性。 ... [详细]
  • JVM参数设置与命令行工具详解
    JVM参数配置与命令行工具的深入解析旨在优化系统性能,通过合理设置JVM参数,确保在高吞吐量的前提下,有效减少垃圾回收(GC)的频率,进而降低系统停顿时间,提升服务的稳定性和响应速度。此外,本文还将详细介绍常用的JVM命令行工具,帮助开发者更好地监控和调优JVM运行状态。 ... [详细]
  • MongoDB Aggregates.group() 方法详解与编程实例 ... [详细]
  • Java新手求助:如何优雅地向心仪女生索要QQ联系方式(附代码示例与技巧)
    在端午节后的闲暇时光中,我无意间在技术社区里发现了一篇关于如何巧妙地向心仪女生索取QQ联系方式的文章,顿时感到精神焕发。这篇文章详细介绍了源自《啊哈!算法》的方法,不仅图文并茂,还提供了实用的代码示例和技巧,非常适合 Java 新手学习和参考。 ... [详细]
  • 在 Linux 系统中,`/proc` 目录实现了一种特殊的文件系统,称为 proc 文件系统。与传统的文件系统不同,proc 文件系统主要用于提供内核和进程信息的动态视图,通过文件和目录的形式呈现。这些信息包括系统状态、进程细节以及各种内核参数,为系统管理员和开发者提供了强大的诊断和调试工具。此外,proc 文件系统还支持实时读取和修改某些内核参数,增强了系统的灵活性和可配置性。 ... [详细]
  • 深入解析零拷贝技术(Zerocopy)及其应用优势
    零拷贝技术(Zero-copy)是Netty框架中的一个关键特性,其核心在于减少数据在操作系统内核与用户空间之间的传输次数。通过避免不必要的内存复制操作,零拷贝显著提高了数据传输的效率和性能。本文将深入探讨零拷贝的工作原理及其在实际应用中的优势,包括降低CPU负载、减少内存带宽消耗以及提高系统吞吐量等方面。 ... [详细]
  • 如何使用 org.geomajas.configuration.FontStyleInfo.getColor() 方法及其代码示例详解 ... [详细]
  • 本文探讨了将PEBuilder转换为DIBooter.sh的方法,重点介绍了如何将DI工具集成到启动层,实现离线镜像引导安装。通过使用DD命令替代传统的grub-install工具,实现了GRUB的离线安装。此外,还详细解析了bootice工具的工作原理及其在该过程中的应用,确保系统在无网络环境下也能顺利引导和安装。 ... [详细]
  • Docker网络基础探讨了如何通过高效的技术手段实现跨主机容器间的顺畅通信与访问。本文深入分析了Docker网络架构,特别是其在多主机环境下的应用,为Go语言开发者提供了宝贵的实践指导和理论支持。 ... [详细]
  • 在探讨链表之前,我们先讨论一个编程中常见的问题:如何在程序执行过程中有效管理变量。当需要在后续代码中继续使用某个变量时,可以通过局部变量来保存其值。然而,对于更复杂的数据管理和动态调整的需求,链表则提供了一种高效的解决方案。本文将详细介绍链表的设计原理,并通过Java语言手动实现`LinkedList`类,帮助读者深入理解链表的工作机制及其应用场景。 ... [详细]
author-avatar
老男孩2702938107
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有