java 锁

java 锁

什么是锁

  • 在单进程的进程中,当多个线程可以同时改变某个单独存在的共享变量时,需要对变量或者代码块进行同步,从而在修改变量是能够灵活的执行删除变量的同时修改
  • 为了实现多个个体在同一时间点,代码块一个线程可以实现,需要在特定的地方做一个标记,标记方式才能看到,发现已经有标记了,那么即将到来标记的线程同步代码块取消之间可以结束实验标记,这个标记为锁
  • 不同的地方实现锁的方式也不一样,只要能看到所有线程都可以得到满足,如java中同步时在对象头标记,锁接口的实现类实际上是在一个动态类型变量,其日常快速生命都可以知道的,内核中也是利用相互排斥量或者信号量等数据标记
  • 利用内存数据做锁柜,实际上互不互据的做锁,如结合做水中流水号和时间结合可以做锁,或者是一个不会释放只需要满足在对象标记进行修改,才能保证原子性和内存可见性
  • 什么是锁,千万不要被学的内容限制了锁的概念,只要能够完成锁功能的任何事物都可以称为锁,思想要放开

为什么会用到锁

  • 一个分布式系统不可能同时满足任何一个关系(整体),可用性(availability)和局部容错性(partition tolerance),最多只能满足整个环境
  • 现在很多的大型网站大多应用了故事情节中的故事,一直是一个重要的故事,在互联网领域的需要的场景中,需要有互联网的人来系统的高可用性,系统往往只需要最终的结果

布景

  • 主要之多个服务模式下,同时有多个服务开启
  • 在许多场景中,我们是为了保证数据的最终结果,需要很多的技术方案来支持,比如说用假锁等,但是很多情况并没有那么简单:
    • 与单机最大的不同的不仅仅是多线程,还有各个服务之间的异步
    • 线程因为可以共享堆内存,所以可以简单的移动内存作为标记存储位置
  • 我扩充一点,在线程之下,还有协程,协程比线程还要小,协程是属于线程的,类似于线程属于协程

什么是嵌入式锁

  • 当在模拟模型下,数据只有一个(或者有限制),此时需要利用锁的控制时刻的过程数据
  • 与单机模式下的锁需要保证进程可见,还需要考虑进程与锁之间的网络问题
  • 有一个很重要的问题,有网络之后我们要考虑到网络的时延等问题
  • 应用锁还是可以将标记内存,只是将内存不是某个内存在进程分配的公共内存,如redis,memcache。至于利用数据库,等做锁与单机的实现是一样的,只要保证标记能互通排斥就可以

什么样的锁符合需求

  • 可以支持分布式,能够在同一时间被同一台机器上的一个线程执行
  • 是可重入的,为了避免死锁
  • 锁最好是一把锁,可以适应各种场景
  • 是一把公平的锁,可以根据场景考虑用不用
  • 有很好的获取锁和释放锁的功能
  • 获取锁和释放锁的性能很好

基于数据库的应用锁

基于数据库,如MySQL

  • 基于数据库表

    • 要实现分布式锁,最简单的方式可能就是直接创建一张锁表,然后通过操作该表中的数据来实现了
    • 当我们要锁住某个方法或资源时,我们就在该表中增加一条记录,想要释放锁的时候就删除这条记录
      我们对特定记录做了唯一性约束,这里如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成
  • 存在问题

    • 这把锁强依赖数据库的可用性,数据库是一个单点,一旦数据库挂掉,会导致业务系统不可用
    • 这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁
    • 这把锁只能是非阻塞的,因为数据的insert操作,一旦插入失败就会直接报错。没有获得锁的线程并不会进入排队队列,要想再次获得锁就要再次触发获得锁操作
    • 这把锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在了
  • 基于数据库排他锁

    • 基于MySql的InnoDB引擎
    • 查询语句后面增加for update,数据库会在查询过程中给数据库表增加排他锁
    • 可以通过connection.commit()操作来释放锁
 public boolean lock(){
    connection.setAutoCommit(false)
    while(true){
        try{
            result = select * from methodLock where method_name=xxx for update;
            if(result==null){
                return true;
            }
        }catch(Exception e){
 
        }
        sleep(1000);
    }
    return false;
}
  • 总结
    • 这两种方式都是依赖数据库的一张表
    • 一种是通过表中的记录的存在情况确定当前是否有锁存在,另外一种是通过数据库的排他锁来实现分布式锁
    • 数据库实现分布式锁的优点:直接借助数据库,容易理解
    • 数据库实现分布式锁的缺点:会有各种各样的问题,在解决问题的过程中会使整个方案变得越来越复杂
    • 操作数据库需要一定的开销,性能问题需要考虑
    • 使用数据库的行级锁并不一定靠谱,尤其是当我们的锁表并不大的时候
    • 分布式效果最差的实现方式,不建议使用

基于缓存,如Redis

  • setnx()
    • setnx()的含义就是SET if NOT EXISTS,其主要有两个参数setnx(key,value)。该方法是原子的,如果key不存在,则设置当前key成功,返回1,如果key存在,则设置当前key失败,返回0
  • expire()
    • expire设置过期时间,要注意设置x不能设置key的超过时间,通过expire()来对key进行设置
  • 我来通俗的解释下,setnx函数是用来设置锁谁使用的,expire函数设置时间,一个执行,一个控制
  • 使用步骤
    • setnx(localkey,1)如果返回0,则说明占位失败,如果返回1,则说明占位成功
    • expire设置过期时间,防止死锁问题
    • 执行业务后,可以通过删除命令删除key

基于Zookeeper、etcd等

  • zookeeper相关基础知识
    • ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务
    • 是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件
    • 它是一个典型的分布式数据一致性解决方案
    • 分布式应用程序可以基于Zookeeper实现诸如数据发布/订阅,负载均衡,命名服务,分布式协调/通知,集群管理,Master选举,分布式锁和分布式队列等功能
    • 主要是用来解决分布式应用中经常遇到的一些数据管理问题
  • zookeeper锁相关基础知识
    • ZooKeeper的每一个节点,都是一个天然的顺序发号器
    • ZooKeeper节点的递增有序性,可以确保锁的公平
    • ZooKeeper的节点监听机制,可以保障占有锁的传递有序而且高效
    • ZooKeeper的节点监听机制,能避免羊群效应
  • 实现步骤
    • 在lock节点下创建一个临时节点
    • 判断创造的新生顺序是否最小,是最小的则获得成功,不是则获取失败,然后看顺序比前一个阶段
    • 当获取锁失败,设置手表后则手表事件发生后,开始判断是否顺序小
    • 取锁成功则执行代码,最后释放锁(删除该节点)
  • zookeeper锁优缺点
    • 优点:ZooKeeper分布式锁(如InterProcessMutex),能有效的解决分布式问题,不可重入问题,使用起来也较为简单
    • 缺点:ZooKeeper实现的分布式锁,性能并不太高

韩清宗

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值