9月 132019
课程链接
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的可以用于哪些场景?
- 缓存
- 消息队列
- 分布式锁
- 发布订阅