lock接口最常见的实现类ReentrantLock,通常情况下,lock只允许一个线程访问共享资源,也有特殊情况,比如读写锁里的读锁。
lock和synchronizrd是常见的锁,都可以让代码变得安全。但是功能上有差别,二者不是能替代的,而是看场景。
为什么synchronizrd不够用?
1.效率低:锁的释放情况少,试图获得锁时不能设定超时,不能中断一个正在试图获得锁的进程。
2.不够灵活:(读写锁更灵活)加锁和释放锁多时机单一,每个锁仅有单一的条件。
3.无法知道synchronizrd是否成功获得锁
Lock主要方法介绍
获取lock锁的四种方法:lock(),trylock(),trylock(time,unit),lockInterruptibly()
lock()
最普通的锁,如果锁被其他线程获取,则等待。如果有异常他不会主动释放。所以最佳实践就是tey catch,在finally里释放锁。lock()不能被中断,一旦陷入死锁,他就会永久等待。
trylock()
用来尝试获取锁,返回值是布尔类型,如果锁被占用则返回false,没有则成功。该方法直接返回。
trylock(time,unit)
和trylock()类似,只不过会等待time的时间
lockInterruptibly()
相当于超时时间设置为无限。
可见行保证
lock和synchronizrd都有对可见行的保证。
锁的分类
有可能一个锁对应多个类型,也有可能一个类型对应多个锁。比如ReentrantLock既是互斥锁,又是可重入锁,可中断锁。
乐观锁和悲观锁
乐观锁非互斥同步,悲观锁是互斥同步
互斥同步劣势:阻塞和唤醒带来的性能劣势。永久阻塞。优先级反转。
乐观锁例子:原子类,并发容器等。悲观锁:synchronizrd和lock
悲观锁:一劳永逸,消耗资源少,适合并发写入多的情况,竞争激烈的。
乐观锁:并发少,多读懂场景,消耗资源递增,因为需要判断。
可重入锁和非可重入锁
可重入:我拿到锁了,我还可以继续拿这把锁。好处避免死锁,提高封装型。
底层是AQS
公平锁和非公平锁
公平锁:按照线程请求顺序来分配锁。非公平锁:不完全按照顺序来,可以按照规则插队
为什么会有非公平锁?因为唤醒线程有空档期,为了增加吞吐量,所以可以插队执行。
共享锁和排它锁
共享锁:又称读锁,可以查看,无法修改和删除。排他锁:写锁。典型例子是ReentrantReadWriteLock就是。
规则:多个线程只申请读锁,没问题。如果有一个线程占用了读锁,就不能写了,等锁释放才行。如果有写的线程,别的线程也要等待。
读锁插队策略:策略1读可以插队,效率高,容易造成饥饿问题。策略2:写之后的线程都需要等待。避免饥饿。ReentrantReadWriteLock是策略2
读写锁只能降级不能升级。除非读锁全部解锁,可以升级。容易造成死锁。
可中断锁
定义:某一个线程正在执行锁中的代码,另一个线程等在获取锁,但是等的时间久了,不想等了,我们可以中断他,这种就是可中断锁。lock是可中断锁,而synchronizrd不是可中断锁。