背景

今天做需求时遇到一个统计场景,接口将用户请求记录缓存在concurrentHashmap,其中用户名作为map的Key,value为统计结果类的对象,更新此map的时候使用分段锁(通过用户名取hash值定位对应的锁)确保在相对良好性能下使得value更新线程安全。此外通过定时任务2秒一次将缓存的map保存到redis数据库后再清空map,这意味着定时任务执行的某个时候需要暂停所有写入map操作,由于map写入是使用分段锁,意味着需要阻塞所有获取分段锁线程,类似于mysql某些修改表结构的时候会阻塞行锁一样。查阅资料后发现java并无相关实现,这里手动实现一个。

分段独占锁

  1. 首先实现分段独占锁,在表-行锁中充当行锁的功能
  2. 代码如下,通过对key取hash值来定位具体的某行(锁)
public class SegReentrantLock{private List<Lock> lockList;public SegReentrantLock() {this(10);}public SegReentrantLock(int size) {if (size < 1){throw new IllegalArgumentException("size must great than 0");}this.lockList = new ArrayList<>(size);for (int i = 0; i < size; i++) {this.lockList.add( new ReentrantLock() );}}public void lock(String key){int lockIndex = this.getLockIndex(key);this.lockList.get(lockIndex).lock();}public void unlock(String key){int lockIndex = this.getLockIndex(key);this.lockList.get(lockIndex).unlock();}private int getLockIndex(String key){int lockIndex = key.hashCode() % this.lockList.size();return Math.abs(lockIndex);}public boolean tryLock(String key, long time, TimeUnit timeUnit) throws InterruptedException {int lockIndex = key.hashCode() % this.lockList.size();if (time < 1 || timeUnit == null){return this.lockList.get(lockIndex).tryLock();}return this.lockList.get(lockIndex).tryLock(time, timeUnit);}public boolean tryLock(String key) throws InterruptedException {return this.tryLock(key, 0, TimeUnit.SECONDS);}
}

行表锁

  1. 在实现了分段锁的基础上,通过jdk自带StampedLock来模拟表锁。
  2. 获取行锁的时候,先获取StampedLock的读锁,再获取行锁,释放同理
  3. 获取表锁的时候,直接获取StampedLock的写锁即可,如对性能有高要求,需减少获取表锁的饥饿现象,可通过StampedLock的tryOptimisticRead方法将加锁逻辑修改为“读的过程中也允许获取写锁后写入”的模式。
  4. 行表锁代码如下
public class TableRowsLock {//表锁private final StampedLock STAMPED_LOCK = new StampedLock();//行锁private SegReentrantLock rowLocks;public TableRowsLock() {this(10);}public TableRowsLock(int rows) {rowLocks = new SegReentrantLock(rows);}/*** @description:释放表锁* @param time* @param timeUnit* @return long 时间戳,为0表示加锁失败*/public long tryLockTable(int time, TimeUnit timeUnit) throws InterruptedException {return STAMPED_LOCK.tryWriteLock(time, timeUnit);}public long tryLockTable() throws InterruptedException {return STAMPED_LOCK.tryWriteLock(0, TimeUnit.SECONDS);}/*** @description:释放表锁* @param stamp 获取表锁返回的时间戳* @see #tryLockTable(int, TimeUnit) * @return void*/public void unLockTable(long stamp){STAMPED_LOCK.unlockWrite(stamp);}/*** @description:加行锁* @param key* @param time* @param timeUnit* @return long 时间戳,为0表示加锁失败*/public long tryLockRow(String key, int time, TimeUnit timeUnit) throws InterruptedException {//先设置表锁为已读状态long stamp = STAMPED_LOCK.tryReadLock(time, timeUnit);//失败,直接返回if (stamp == 0) return stamp;//锁行boolean lockedRow = rowLocks.tryLock(key, time, timeUnit);if (!lockedRow){STAMPED_LOCK.unlockRead(stamp);return 0;}return stamp;}public long tryLockRow(String key) throws InterruptedException {return tryLockRow(key, 0, TimeUnit.SECONDS);}/*** @description:释放行锁* @param key* @param stamp 获取行锁获得的时间戳* @see #tryLockRow(String, int, TimeUnit)*/public void unlockRow(String key, long stamp){STAMPED_LOCK.unlockRead(stamp);rowLocks.unlock(key);}
}

测试代码

public static void main(String[] args) {//测试行锁释放和未释放情况下获取表锁testBlockingTableLock();//测试表锁释放和未释放情况下获取行锁
//        testBlockingRowLock();}//测试行锁释放和未释放情况下获取表锁private static void testBlockingTableLock(){TableRowsLock tableLock = new TableRowsLock(2);new Thread(() -> {try {long stamp1 = tableLock.tryLockRow("1");System.out.println("未加表锁与行锁时获取行锁1,结果:" + stamp1);long stamp2 = tableLock.tryLockRow("2");System.out.println("未加表锁只加行锁时获取行锁2,结果:" + stamp2);TimeUnit.SECONDS.sleep(2);//释放行锁tableLock.unlockRow("1", stamp1);tableLock.unlockRow("2", stamp2);} catch (Exception ex){ex.printStackTrace();}}).start();new Thread(() -> {try {TimeUnit.MILLISECONDS.sleep(500);//获取表锁long stamp = tableLock.tryLockTable();System.out.println("行锁未释放,获取表锁,结果:" + stamp);TimeUnit.SECONDS.sleep(3);stamp = tableLock.tryLockTable();System.out.println("行锁全部释放后 再次获取表锁,结果:" + stamp);} catch (InterruptedException e) {e.printStackTrace();}}).start();}//测试表锁释放和未释放情况下获取行锁private static void testBlockingRowLock(){TableRowsLock tableLock = new TableRowsLock(2);new Thread(() -> {try {long stamp = tableLock.tryLockTable();System.out.println("未加行锁时获取表锁,结果:" + stamp);TimeUnit.SECONDS.sleep(2);//释放表锁tableLock.unLockTable(stamp);} catch (Exception ex){ex.printStackTrace();}}).start();new Thread(() -> {try {TimeUnit.MILLISECONDS.sleep(500);//获取行锁long stamp = tableLock.tryLockRow("1");System.out.println("表锁未释放,获取行锁,结果:" + stamp);//表锁释放后获取行锁TimeUnit.SECONDS.sleep(3);stamp = tableLock.tryLockRow("1");System.out.println("表锁释放后,再次获取行锁,结果:" + stamp);} catch (Exception e) {e.printStackTrace();}}).start();}

java实现表锁行锁相关推荐

  1. MySQL中的表锁行锁共享锁很难吗?看了本文就清楚了哦

      MySQL数据库中的锁还是非常重要的,本文重点给大家详细的来介绍下MySQL数据中的各种锁. 一.表锁和行锁 1.表锁 表锁的优势:开销小:加锁快:无死锁 表锁的劣势:锁粒度大,发生锁冲突的概率高 ...

  2. mysql怎么加全局锁_MySQL锁机制/管理(并发锁,行锁,表锁,预加锁,全局锁等等)

    MySQL实验室 1.?MySQL 中并发和隔离控制机制 Meta-data元数据锁:在table cache缓存里实现的,为DDL(Data Definition Language)提供隔离操作.一 ...

  3. mysql 表锁 MDL锁 行锁

    mysql 按照粒度分可以分为 全局锁 表锁 行锁 全局锁 给整个锁加上锁 Flush tables with read lock 让数据处于只读的状态 一般用户 场景:做全库的逻辑备份. 表锁 顾名 ...

  4. 小福利,采用excel函数制作大屏可视化,用sumifs函数快速统计汇总数据,锁行锁列以及锁列不锁行

    小福利,采用excel函数制作大屏可视化,用sumifs函数快速统计汇总数据,锁行锁列以及锁列不锁行 源数据如下图所示 第一步处理源数据的效果是下图 其中根据年份变化,求总订单量.总销量.总销售额,只 ...

  5. mysql某个表被行锁了_MySQL中的锁(表锁、行锁)

    锁是计算机协调多个进程或纯线程并发访问某一资源的机制.在数据库中,除传统的计算资源(CPU.RAM.I/O)的争用以外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是所在有数 ...

  6. MySQL高级 - 锁 - InnoDB行锁 - 行锁升级为表锁

    无索引行锁升级为表锁 如果不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,实际效果跟表锁一样. 查看当前表的索引 : show index from test_innodb_lock ...

  7. mysql某个表被行锁了_MySQL 行锁和表锁的含义及区别详解

    一.前言 对于行锁和表锁的含义区别,在面试中应该是高频出现的,我们应该对MySQL中的锁有一个系统的认识,更详细的需要自行查阅资料,本篇为概括性的总结回答. MySQL常用引擎有MyISAM和Inno ...

  8. mysql某个表被行锁了_一文搞懂MySQL行锁、表锁、间隙锁详解

    准备工作 创建表 tb_innodb_lock drop table if exists test_innodb_lock; CREATE TABLE test_innodb_lock ( a INT ...

  9. mysql进阶: mysql中的锁(全局锁/表锁/行锁/间隙锁/临键锁/共享锁/排他锁)

    锁在生活中处处可见,门锁,手机锁等等. 锁存在的意义是保护自己的东西不被别人偷走/修改. 在mysql中锁的意义也是一样,是为了保护自己的数据不被别人进行修改,从而导致出现脏读,幻读等问题.在学习锁的 ...

最新文章

  1. 你知道如何在springboot中使用redis吗
  2. 在RHEL5下构建基于系统用户的Postfix邮件系统
  3. oraclf 复杂查询练习_刷完这些烧脑的SQL练习题,复杂查询才能熟能生巧
  4. ( )不是对网络模型进行分层的目标。
  5. 韩拓-七牛产品演进之路
  6. 【转】HMAC哈希消息认证码及算法原理
  7. Hbase 二级索引 Solr int字段排序问题 can not sort on multivalued field
  8. 计算机无法计算,计算器不能执行计算功能,运算结果始终为0
  9. C++学习系列笔记(四)
  10. hdu 1709 母函数变形
  11. 西门子PLC开发笔记(一):PLC介绍,西门子S1200系列接线、编程、下载和仿真
  12. 学习spark的网站
  13. ps 透明底和改变颜色
  14. AT32F435_437_USB_MSC_SDIO
  15. zheng-环境搭建及系统部署文档
  16. 物联网实战项目 免费领取!手把手教你如何从0搭建智慧家居中的视频监控系统
  17. C#中使用Invalidate()与MouseMove方法刷新控件时,控件闪烁问题解决方法
  18. Lodop 动态加载模板,动态加载数据
  19. 在VMware下安装中标麒麟操作系统6.7版本
  20. 日志审计系统的基本原理与部署方式

热门文章

  1. XAML概要--事件和后台代码
  2. html 接收 图片流_Microsoft Flow 利用自动化工作流增强Power BI使用效率
  3. python语言format用法_python基础_格式化输出(%用法和format用法)
  4. c语言我国有13亿人口,计算机二级循环结构课件.ppt
  5. android 许可协议,Android 基本控件的使用二(注册许可协议)(CheckBox)
  6. 织梦百度php主动推送代码示例,织梦百度php主动推送代码示例,亲试绝对成功!...
  7. 脑电信号特征提取算法c语言_应用深度学习EEGNet来处理脑电信号
  8. oracle基础教程实验,Oracle 11g 基础教程与实验指导(配光盘)(清华电脑学堂)
  9. ios弧形进度条_ios 圆形进度条
  10. 设置代理_如何防止用户更改Windows 10上的代理设置