java锁
synchronized
在jdk 1.5以后,优化了,使其性能并不是像很多帖子说的那样,“非常重”
JUC lock
方法
说明
lock()
获取锁,如果锁被暂用则一直等待
tryLock()
如果获取锁的时候锁被占用就返回false,否则返回true
tryLock(long time, TimeUnit unit)
比起tryLock,多出等待时间
unLock()
lockInterruptibly()
lock与synchronized区别
lock是接口,syn是java关键字,是内置实现;
sync 发生异常时自动释放线程占有的锁,因此不会导致死锁现象发生;lock在发生异常时,如果没有unLock去释放锁,可能造成死锁。因此使用lock时需要在finally中释放锁
lock 可以让等待锁的线程响应中断;sync 不行,使用sync等待线程一直等待下去,不能中断
lock可以知道有没有成功获得锁,sync则无法知道
lock可提高多线程读效率
性能上,竞争不激烈,两者性能;竞争激烈时,lock性能远由于sync。具体需要根据情况而定。
并不一定总是需要用lock,sync更方便,lock需要更精心维护代码、避免死锁
分布式锁
push
pull
的方式
惊群效应
分布式锁需要具备的条件:
互斥性
可重入
超时高效
阻塞/非阻塞
几种实现方式:
数据库实现(乐观锁)
基于zookeeper的实现
基于redis的实现
自研分布式锁(google的chubby, zookeeper实际上是基于这个chubby)
基于数据库实现
略
基于redis的实现
基本命令
SETNX key value
if key不存在,则设置为value;存在则不做任何操作
“SET if Not eXist”
expire key seconds
设置过期时间,如果key已国企,则将会被自动删除。
del key
删除key
实现方式
基本锁
原理:利用redis的setnx,如果不存在某个key则设置值,设置成功则表示锁成功
缺点:如果获取锁后的进程在没有执行完就挂了,则锁永远不会释放
改进型
改进:在基本形式锁上setnx后设置expire,保证超时后也能自动释放锁
缺点:setnx与expire不是一个原子操作,可能执行完setnx该进程就挂了
再改进
lua执行具有原子性
改进:利用lua脚本,将setnx与expire编程一个原子操作,可解决一部分问题
缺点:还是会出现锁过期的问题
具体实现
官方提供的java组件:redisson
redisson的分布式可重入锁RLock java对象实现了java.util.concurrent.locks.lock接口同时还支持过期解锁
地址:
分布式锁方案比较
从理解的难易程度(从低到高)
数据库 > 缓存 > zookeeper
从实现的复杂性角度
zookeeper >= 缓存 > 数据
从性能角度(从高到底)
缓存 > zookeeper >= 数据库
从可靠性角度(从高到低)
zookeeper > 缓存 > 数据库
扩展
redis的可以用于哪些场景?
缓存
消息队列
分布式锁
发布订阅