ReentrantLock
重⼊锁可以完全替代synchronized关键字。在JDK
5
.
0
的早期版本中,重⼊锁的性能远远好于synchronized,但从JDK6
.
0
开始,JDK在synchronized上做了⼤量的优化,使得两者的性能差距并不⼤。重⼊锁对逻辑控制的灵活性要远远好于synchronized。
常⽤⽅法
- void lock():获得锁,如果锁已经被占⽤,则等待。
- void lockInterruptibly():获得锁,但优先响应中断。
- boolean tryLock():尝试获得锁,如果成功,返回true;如果失败则返回false;获得不到锁,则不进
- ⾏等待,⽴即返回。
- boolean tryLock(long time, TimeUnit unit):在给定时间内尝试获得锁。
- boolean isHeldByCurrentThread():判断担⼼线程是否持有锁。
- void unlock():释放锁。
下⾯是使⽤重⼊锁的简单示例:
之所以称之为
重⼊锁
,就是⼀个线程允许反复进⼊。当然,这⾥的反复仅仅局限于⼀个线程;如果同⼀个线程多次获锁,那么在释放锁的时候,也必须释放相同次数。如果释放锁的次数多,那么会得到⼀个
java.lang.IllegalMonitorStateException
异常,反之,如果释放锁的次数少,那么相当于线程还持有这个锁。如下所示:
lockInterruptibly()
如果使⽤synchronized,要么获得锁,要么保持等待。⽽如果使⽤了重⼊锁,则提供了另⼀种可能,那就是线程可以被中断。也就是在等待锁的过程中,程序可以根据需要取消对锁的请求。即:
如果⼀个线程正在等待锁,那么它依然可以收到⼀个通知,被告知⽆须再等待,可以停⽌⼯作了
。可以很好的应对死锁问题。示例如下所示:
tryLock(long time, TimeUnit unit)
除了等待外部通知之外,要避免死锁还有另外⼀种⽅式,就是
限时等待
。以下⾯为例,线程尝试获得锁,如果没有获得锁,则等待5
秒钟。如果
5
秒钟之后依然没有获得锁,则返回false,表示获得锁失败。
tryLock()⽅法也可以不带参数直接运⾏。在这种情况下,当前线程会尝试获得锁,如果锁并未被其他线程占⽤,则申请锁会成功,并⽴即返回true。如果锁被其他线程占⽤,则
当前线程不会进⾏等待,⽽是⽴即返回false
。这种模式不会引起线程等待,因此也不会产⽣死锁。
公平锁和⾮公平锁
在⼤多数情况下,锁的申请都是⾮公平锁。系统只是会从这个锁的等待线程中随机选择⼀个。类似⼤家买票不去排队,乱哄哄的围在售票窗⼝前,售票员忙得焦头烂额,也顾不及谁先谁后,随便找⼀个⼈出票就完事⼉了。
当⼊参为true时,则采⽤公平锁⽅式。要求系统维护⼀个有序队列,因此公平锁的实现成本⽐较⾼,性能相对也⾮常低下。因此,默认情况下,锁是⾮公平的。如果没有特别的需求,也不需要使⽤公平锁。