ReentrantLock简介
可重入锁,作用是使线程安全。对比于sychronized
,它能具有以下特点
- 减小资源锁的力度
- 更可控,减少发生死锁的概率
- 加锁、释放锁都是显示控制的
- 添加锁的作用时间来防止发生死锁
更加灵活
重入锁
可重入锁可以理解为锁的一个标识。该标识具备计数器功能。标识的初始值为0,表示当前锁没有被任何线程持有。每次线程获得一个可重入锁的时候,该锁的计数器就被加1。每次一个线程释放该所的时候,该锁的计数器就减1。前提是:当前线程已经获得了该锁,是在线程的内部出现再次获取锁的场景
ReentrantLock扩展功能
实现可轮询的锁请求
在内部锁中,要恢复死锁的唯一方法就是重启应用;而通过ReentrantLock可以规避死锁的发生
如果你不能获得所有需要的锁,那么使用可轮询的获取方式使你能够重新拿到控制权,它会释放你已经获得的这些锁,然后再重新尝试。可轮询的锁获取模式,由tryLock()方
法实现。此方法仅在调用时锁为空闲状态才获取该锁。如果锁可用,则获取锁,并立即返回值true。如果锁不可用,则此方法将立即返回值false。
```java
/*Acquires the lock only if it is not held by another thread at the timeof invocation.
*/
lock.tryLock()
```
实现可定时的锁
当使用内部锁时,一旦开始请求,锁就不能停止了,所以内部锁给实现具有时限的活动带来了风险。为了解决这一问题,可以使用定时锁。当具有时限的活
动调用了阻塞方法,定时锁能够在时间预算内设定相应的超时。如果活动在期待的时间内没能获得结果,定时锁能使程序提前返回。可定时的锁获取模式,由tryLock(long, TimeUnit)
方法实现。
实现可中断的锁获取请求
可中断的锁获取操作允许在可取消的活动中使用。lockInterruptibly()方法能够使你获得锁的时候响应中断。
ReentrantLock 与 synchronized的比较
相同点
ReentrantLock提供了synchronized类似的功能和内存语义。(提供了对资源加锁的功能)
不同点
与synchronized相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候,锁投票。
ReentrantLock还提供了条件Condition,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock更加适合(下面会阐述Condition)。
ReentrantLock提供了可轮询的锁请求。它会尝试着去获取锁,如果成功则继续,否则可以等到下次运行时处理,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以相比synchronized而言,ReentrantLock会不容易产生死锁些。
ReentrantLock支持更加灵活的同步代码块,但是使用synchronized时,只能在同一个synchronized块结构中获取和释放。注:ReentrantLock的锁释放一定要在finally中处理,否则可能会产生严重的后果。
示例
当使用lock的时候,不能使用 CountDownLatch 来进行倒计时操作,会报出IllegalMonitorStateException
异常。因为当线程被唤醒后,资源信息与lock对象已经不是一一对应的关系,可能出现资源被A线程加锁,但是线程B尝试去解锁。
/** * @author: n * @date: 2019/5/13:上午11:41 */ public class ReentrentLockTest implements Runnable{ private ReentrentLockDemo demo = new ReentrentLockDemo(10); private static ReentrantLock lock = new ReentrantLock(); static CountDownLatch begin = new CountDownLatch(1); static CountDownLatch end = new CountDownLatch(10); @Override public void run() { try { String threadName = Thread.currentThread().getName(); System.out.println(threadName + "-->调用run方法被阻塞跳出run线程回到main方法线程"); // begin.await(); //lock.tryLock(); int num = demo.splice(); System.out.println(threadName + "--->" + num); end.countDown(); } catch (Exception e) { e.printStackTrace(); }finally { //lock.unlock(); } } public static void main(String[] args) throws Exception { ReentrentLockTest test = new ReentrentLockTest(); for (int i = 0; i < 10; i++) { Thread t = new Thread(test); /** * 进入到run方法,run方法被begin阻塞。run方法退出,重新回到main中。从而完成对所有线程的初始化 * * */ // System.out.println(“”); t.start(); } // 调用countDown。所有线程被唤醒,开始执行run方法 begin.countDown(); end.await(); System.out.println("end"); } }
来源:https://www.cnblogs.com/KevinStark/p/10857578.html