大家好,我是wave。这次我们继续接着讲锁,来给大家聊一聊Lock的一些底层原理。
基本使用
Lock的基本使用案例
public class Solution {
static int n = 0;
public static void main(String[] args)throws Exception {
//创建一个Lock对象
Lock lock = new ReentrantLock();
//创建10个线程对n自加10000
for (int i = 0; i < 10; i++) {
new Thread(()->{
//加锁
lock.lock();
try {
for (int j = 0; j < 10000; j++) {
n++;
}
}catch (Exception e){
e.printStackTrace();
}finally {
//解锁
lock.unlock();
}
}).start();
}
Thread.sleep(3000);
System.out.println(n);//100000
}
}
- 这个代码案例就是创建了10个线程对一个变量n进行自加10000的操作,然后使用了Lock进行加锁,最后结果是正确的100000。
- 可以看到这里Lock加锁的逻辑代码加了一个try-catch块,这里并不是必须的一个异常捕获,但是Lock比较标准的写法就是最好使用try-catch块写入业务逻辑,最后在finally中进行unlock(解锁)。这样做的好处是避免某个线程突然发生异常,导致后面的unlock代码没有执行,就会造成死锁。
- 本篇文章主要讨论的是Lock的实现类ReentrantLock。
加锁操作
进入到lock()方法中,并找到ReentrantLock的实现方法
public void lock() {
sync.lock();
}
继续进入lock()
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
这里我们发现Sync是一个继承了AbstractQueuedSynchronizer的类,AbstractQueuedSynchronizer就是我们常说的AQS,所以说Lock的底层使用的是AQS框架。AQS的细节我们后面继续说。
继续看lock()抽象方法的实现类,我们先选择看公平锁。
final void lock() {
acquire(1);
}
接着看 acquire(1),这个方法的tryAcquire、acquireQueued、addWaiter都会详细分析
public