背景介绍
在JDK1.5之后提供了在协调对共享对象访问时的机制。它提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的语法都是显示的。它是基于AQS实现的并发访问控制
private static int count = 0;public static void main(String[] args) throws InterruptedException {Lock lock = new ReentrantLock();Thread t1 = new Thread(new CountUtil(lock));Thread t2 = new Thread(new CountUtil(lock));t1.start();t2.start();t1.join();t2.join();System.out.println(count);}static class CountUtil implements Runnable{private final Lock lock;public CountUtil(Lock lock) {this.lock = lock;}@Overridepublic void run() {lock.lock();for (int i = 0; i < 100; i++) {count++;}lock.unlock();}}
重入性
ReentrantLock是一个可重入锁。即同一个线程是可以循环加锁的
for (int i = 0; i < 100; i++) {lock.lock();count++;lock.unlock();}
公平性
公平性是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序,也就是FIFO。当构造函数中的参数为true时,即为公平锁
new ReentrantLock(true)
指定时间内尝试加锁
ReentrantLock可以指定一段时间内尝试加锁
Lock lock = new ReentrantLock();boolean result = lock.tryLock(1, TimeUnit.SECONDS);
ReentrantLock与Synchronized区别
- 尝试非阻塞地获取锁:当前线程尝试获取锁,如果这一时刻锁没有被其他线程获取到,则成功获取并尺有锁
- 能被中断地获取锁:与synchronized不同,获取到锁的线程能够相应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁会被释放
- 超时获取锁,在指定的截止时间之前获取锁,如果截止时间到了仍无法获取锁,则返回
加锁原理
核心变量
ReentrantLock底层是基于AQS实现的并发访问控制。AQS的核心成员主要有state(状态)、exclusiveOwnerThread(当前加锁线程)和阻塞队列。
加锁逻辑
非公平锁
线程直接通过cas尝试修改state变量(对其进行加1操作),如果成功则同时将exclusiveOwnerThread(当前加锁线程)设置为自己。如果cas修改state失败,则需要判断当前加锁的线程是否是自己,如果是,对state进行+1操作(可重入,多次进入就是对state+1),如果不是进入阻塞队列等待公平锁
线程在加锁前需要先判断阻塞队列中是否有等待中的线程,如果有就进入阻塞队列,如果没有再通过cas的方式加锁
释放锁
成功加锁的线程每次进入时都会对state进行+1,所以每次释放时就是对state进行-1,如果state=0时则彻底释放锁,并唤起阻塞队列中的头结点进行获取锁
超时获取锁

