基于数据库
    悲观锁(排他锁):使用synchronized 只能锁住单进程中的多个线程,不能锁住多进程中的多个线程,分布锁的方式一是通过数据表来实现
    基于 select * from table where xx=yy for update SQL语句来实现,在查询对象时就加锁,其他线程只能等待,性能非常之低;
    乐观锁-不上锁:表中添加一个时间戳或者版本号的字段来实现,update account set version = version + 1 where id = #{id} and version = #{oldVersion} 当更新不成功,客户端重试,重新读取最新的版本号或时间戳,再次尝试更新,类似 CAS 机制,推荐使用
    数据库对应表中加上version字段,
    基于数据库实现分布式锁:
    利用主键或唯一索引唯一的特性,如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,当方法执行完毕之后,想要释放锁的话,删除这条数据库记录即可
    利用数据库实现分布式锁思想:
    基于数据库主键:
    数据库中的主键是唯一且自增长的,可以利用这一特性,当多个请求同时提交数据时数据库会保证其中 一个操作成功,可以变相认为成功的这个请求线程获取到了锁,方法执行完毕,释放锁,删除数据库记录;
    基于数据库唯一检索:
    name 是锁的名字,唯一索引 ,数据库中的每一条记录就是一把锁,利用的mysql唯一索引的排他性实现分布式锁。
    数据库其他问题解决方式

    • 数据库是单点?搞两个数据库,数据之前双向同步,一旦挂掉快速切换到备库上。
    • 没有失效时间?只要做一个定时任务,每隔一定时间把数据库中的超时数据清理一遍。
    • 非阻塞的?搞一个 while 循环,直到 insert 成功再返回成功。
    • 非重入的?在数据库表中加个字段,记录当前获得锁的机器的主机信息和线程信息,那么下次再获取锁的时候先查询数据库,如果当前机器的主机信息和线程信息在数据库可以查到的话,直接把锁分配给他就可以了。
      • 获取:再次获取锁的同时更新count(+1).
      • 释放:更新count-1,当count==0删除记录。
    • 非公平的?再建一张中间表,将等待锁的线程全记录下来,并根据创建时间排序,只有最先创建的允许获取锁。

    基于redis
    Redis提供了一个命令setnx 可以来实现分布式锁,该命令只在键 key 不存在的情况下 将键 key 的值设置为 value ,若键 key 已经存在, 则 SETNX 命令不做任何动作。根据这一特性我们就可以制定Redis实现分布式锁的方案了。
    简单理解就是 :如果三个服务同时抢锁,服务A抢先一步执行setnx(lock_stock,1)加上锁,那么当服务B在执行setnx(lock_stock,1)加锁的时候就会失败,服务C也一样,服务A抢到锁执行完业务逻辑后就会释放锁,可以使用del(lock_stock)删除锁,其他服务就可以执行setnx(lock_stock,1)加锁了
    死锁问题以及解决:
    产生原因:获取锁的服务在释放锁的时候宕机了;
    解决方式:在获取锁的时候需要设置锁的自动超时时间,当锁到达超时时间时将锁关闭;自动超
    时:也就是Key的超时自动删除,即使服务宕机没有调用del释放锁,那么锁本身也有超时
    时间,可以自动删除锁,别的服务就可以获取锁了
    原子性问题:
    就是setnx获取锁和expire不是原子性操作,假设有一极端情况,当线程通过setnx(lock_stock,1)获取到锁,还没来得及执行expire(lock_stock,30)设置锁的过期时间,服务就宕机了,这个锁到底该不该释放,又造成了死锁问题
    解决方式:
    使用SET key value [EX seconds] [PX milliseconds] [NX|XX] 命令进行锁的创建
    误删问题:
    原因:就是在del释放锁的时候可能会误删除别人加的锁,例如服务A获取到锁lock_stock,过期时间为 5s,如果在服务A执行业务逻辑的这一段时间内,锁到期自动删除,且别的服务获取到了锁lock_stock,那么服务A业务执行完成执行del(lock_stock)是不是会把别人的锁给删除掉呢???
    解决方式:通过添加一个唯一标识让每个业务在删除锁的时候先去判断是不是自己所获取到的锁:我们可以在删除锁的时候先判断一下要删除的锁是不是自己上的锁,比如可以把锁的值使用一个UUID,在释放锁的时候先获取一下锁的值和当前业务中创建的UUID是不是同一个,如果是才执行·del删除锁,当然也可以使用线程的ID替代UUID
    使用Lua脚本保证原子性
    可重入锁
    让未获取到锁的线程等待重新执行获取锁
    Redisson的实现分布式锁
    redisson是实现java操作redis的工具包,它不仅提供了一系列常用的操作Redis的API,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法,Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。
    详情:墨家巨子:https://blog.csdn.net/u014494148/article/details/109791660
    zookeeper的实现分布式锁

    API总结:
    l new ZkClient(“127.0.0.1:2181”,5000); 创建zookeeper客户端
    l client.getChildren(“/“):获取子节点 “/”代表根节点
    l client.createPersistent:创建持久节点
    l client.createPersistentSequential:创建持久有顺节点,会在path后面增加序号
    l client.createEphemeral:创建临时节点
    l client.createEphemeralSequential:创建临时有序节点
    l client.subscribeChildChanges:订阅子节点的改变
    l client.subscribeDataChanges:订阅某个节点的数据改变