一、总述
1.Java java.util.concurrent包下的锁的原理其实都差不多,如下:
AQS原理(共享资源+FIFO先进先出队列)+ LockSupport.park(Thread) 阻塞当前线程+ LockSupport.unpark(Thread) 唤醒队列中的线程
2.Java中锁的阻塞和唤醒底层都是通过LockSupport实现的,LockSupport的底层是Unsafe接口,Unsafe是一个提供硬件级别的原子操作相关的接口,可实现线程的挂起及恢复,还可实现多线程同步(包括锁机制、CAS)等操作。
3.其中ReentrantLock的AQS(AbstractQueuedSynchronizer)共享资源为:int state(当前锁是否被持有),Thread exclusiveOwnerThread(持有锁的线程);FIFO先进先出队列数据结构为Node(双向链表)
二、ReetrantLock、ReetrantLock的Condition源码解析
1.我们按照下边代码执行顺序进行源码解析(此处解析的是ReetrantLock默认的非公平锁)
非公平锁就是 thread1获取到锁,thread2在队列中排队等待获取锁,thread1释放锁时,这时候thread3也正好想要获取锁,这时候thread2和thread3都有可能获取到锁,即虽然thread2在队列中等了一段时间,但是未入队列的thread3也是可以抢到锁的;
公平锁就是thread1获取到锁,thread2在队列中排队等待获取锁,thread1释放锁时,这时候thread3也正好想要获取锁,却发现队列中已经有线程在等待了,这时候thread3就直接加入队列,所以排在队列前边的thread2获取到锁了。
private final static ReentrantLock lock = new ReentrantLock();
private final static Condition condition = lock.newCondition();
@org.junit.Test
public void testArrayBlockingQueue(){
Thread thread1 = new Thread(() -> {
lock.lock();
try {
System.out.println("thread1 await start.");
condition.await();
System.out.println("thread1 await end.");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("thread1 unlock start.");
lock.unlock();
System.out.println("thread1 unlock end.");
}
});
thread1.setName("thread1");
thread1.start();
//确保thread1先执行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread thread2 = new Thread(() -> {
lock.lock();
try {
System.out.println("thread2 sleep start.");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread2 notify start.");
condition.signal();
System.out.println("thread2 notify end.");
}finally {
System.out.println("thread2 unlock start.");
lock.unlock();
System.out.println("thread2 unlock end.");
}
});
thread2.setName("thread2");
thread2.start();
//确保thread2先执行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread thread3 = new Thread(() -> {
lock.lock();
try {
System.out.println("thread3 do some thing.");
}finally {
System.out.println("thread3 unlock start.");
lock.unlock();
System.out.println("thread3 unlock end.");
}
});
thread3.setName("thread3");
thread3.start();
try {
Thread.sleep(3000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
2.实例化
2.thread1 lock.lock()
thread1获取到锁
2.thread2 lock.lock()
thread2想获取锁,但是由于锁已被thread1获取,所以thread2被挂起并放入线程等待队列,等待锁释放后被唤醒重新获取锁。
3.thread3 lock.lock()
thread3想获取锁,但是由于锁已被thread获取,所以thread3被挂起并放入线程等待队列,等待释放后被唤醒重新获取锁
4.thread1 condition.await()
4.1 thread1 执行condition.await(),
4.1.1 线程进入Condition队列等待被唤醒,
4.1.2 释放锁,
4.1.3 唤醒线程等待队列中的头结点线程即thread2,
4.2 thread2被唤醒后再次尝试获取锁,thread2获取锁成功,
4.3 thread2执行下一步代码condition.signal();
5. thread2 condition.signal()
thread2 执行condition.signal(),将Condition队列中的头结点线程即thread1放入线程等待队列,并唤醒thread1,thread1再次尝试获取锁,但因为thread1此时不是线程等待队列的头结点线程,所以再次阻塞。
6. thread2 lock.unlock(),
thread2执行业务逻辑代码完毕后,thread2执行lock.unlock(),释放锁,并唤醒线程等待队列的头结点即thread3,thread3唤醒后尝试获取锁并获取锁成功,然后执行thread3的业务逻辑代码。
7. thread3 lock.unlock()
thread3执行业务逻辑代码完毕后,thread3执行lock.unlock(),释放锁,并唤醒线程等待队列的头结点即thread1,thread1唤醒后尝试获取锁并获取锁成功,然后执行thread1的业务逻辑代码。
8.thread1 lock.unlock()
thread1执行业务逻辑代码完毕后,thread1执行lock.unlock(),释放锁,此时线程等待队列中无线程节点排队等待获取锁了,全部结束。