目录
6.1 LockSupport 工具类
6.2 独占锁 ReentrantLock 的原理
获取锁
1.void lock() 方法
2.void lockInterruptibly() 方法
3.boolean tryLock() 方法
4.boolean tryLock(long timeout, TimeUnit unit) 方法
释放锁
6.1 LockSupport 工具类
LockSupport 它的主要作用是 挂起和唤醒线程 ,该工具类是创建锁和其他同步类的基础。LockSupport 类与每个使用它的线程都会关联一个许可证,在默认情况下调用 LockSupport 类的方法的线程是不持有许可证的。 LockSupport 是使用 Unsafe 类实现的。
6.2 独占锁 ReentrantLock 的原理
ReentrantLock 是可重入的独占锁,同时只能有一个线程可以获取该锁,其他获取该锁的线程会被阻塞而被放入该锁的 AQS 阻塞队列里面。
Sync 类直接继承自 AQS ,它的子类 NonfairSync 和 FairSync 分别实现了获取锁的非公平与公平策略。默认情况下是非公平的实现。public ReentrantLock() {sync = new NonfairSync();}public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
获取锁
1.void lock() 方法
- 当一个线程调用该方法时,说明该线程希望获取该锁。如果锁当前没有被其他线程占用并且当前线程之前没有获取过该锁,则当前线程会获取到该锁,然后设置当前锁的拥有者为当前线程,并设置 AQS 的状态值为 1【AQS 的 state 状态值表示线程获取该锁的可重入次数】,然后直接返回。
- 如果当前线程之前已经获取过该锁,则这次只是简单地把 AQS 的状态值加 1 后返回。
- 如果该锁已经被其他线程持有, 则调用该方法的线程会被放入 AQS 队列后阻塞挂起。
2.void lockInterruptibly() 方法
该方法与 lock() 方法类似,它的不同在于,它对中断进行响应,就是当前线程在调用该方法时,如果其他线程调用了当前线程的 interrupt ()方法,则当前线程会抛出 InterruptedException 异常,然后返回。
默 认 AQS 的状态值为 0 ,所以第一个调用 Lock 的 线程会通过 CAS 设置状态值为 1 , CAS 成 功 则 表 示 当 前 线 程 获 取 到 了 锁, 然 后 setExclusiveOwnerThread 设置该锁持有者是当前线程。如果这时候有其他线程调用 lock 方法企图获取该锁, CAS 会失败,然后会调用 AQS的 acquire 方法。注意,传递参数为 1 ,final void lock() {//(1)CAS设置状态值if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());else//(2)调用AQS的acquire方法acquire(1); }
3.boolean tryLock() 方法
尝试获取锁,如果当前该锁没有被其他线程持有,则当前线程获取该锁并返回 true , 否则返回 false 。注意,该方法不会引起当前线程阻塞。 tryLock() 使用的是非公平策略。
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
4.boolean tryLock(long timeout, TimeUnit unit) 方法
尝试获取锁,与 tryLock ()的不同之处在于,它设置了超时时间,如果超时时间没有获取到该锁则返回 false 。
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {//调用AQS的tryAcquireNanos方法
return sync.tryAcquireNanos(1, unit.toNanos(timeout));}
释放锁
尝试释放锁,如果当前线程持有该锁,则调用该方法会让该线程对该线程持有的 AQS 状态值减 1,如果减去 1 后当前状态值为 0,则当前线程会释放该锁,否则仅仅减 1 而已。
如果当前线程没有持有该锁而调用了该方法则会抛出 IllegalMonitorStateException 异常
public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);
}
public final void acquireInterruptibly(int arg)throws InterruptedException {//如果当前线程被中断,则直接抛出异常if (Thread.interrupted())throw new InterruptedException();//尝试获取资源if (!tryAcquire(arg))//调用AQS可被中断的方法doAcquireInterruptibly(arg);
}