原理
- 锁获取:无条件、可轮询、可中断
- 锁释放:加锁和解锁都是显式的
获取锁
- CAS操作抢占锁,抢占成功则修改锁的状态为1,将线程信息记录到锁当中,返回state=1
- 抢占不成功时,会再次尝试获取锁资源,如果获取成功直接返回,获取不成功,新建一个节点插入到当前AQS队列的尾部,节点表示唤醒AQS队列中的节点再次尝试去获取锁,这就形成了自旋锁。
释放锁
- 获取锁的状态值,释放锁将状态值-1
- 判断当前释放锁的线程和锁中保存的线程信息是否一致,不一致会抛出异常
ReentrantLock 公平锁与非公平锁
- 公平锁
公平锁的实现就是谁等待时间最长,谁就先获取锁
private static Lock fairLock = new ReentrantLock(true)
- 非公平锁
随机获取的过程,谁运气好,cpu时间片轮询到哪个线程,哪个线程就能获取锁
private static Lock norFairLock = new ReentrantLock()
代码
package com.tomalen.gulimall.product.Thread;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
private static Lock fairLock = new ReentrantLock(true);
private static Lock norFairLock = new ReentrantLock();
public static void main(String[] args) {
new Thread("线程A"){
@Override
public void run() {
test();
}
}.start();
new Thread("线程B"){
@Override
public void run() {
test();
}
}.start();
}
public static void test() {
for (int i = 0; i <= 5; i++) {
try {
norFairLock.lock();
System.out.println(Thread.currentThread().getName() + "获取了锁");
TimeUnit.MILLISECONDS.sleep(100);
}catch (Exception e){
e.printStackTrace();
}finally {
norFairLock.unlock();
}
}
}
}
重入锁与不可重入锁
概念
- 每一个获取锁对应着一条线程及其计数器,当其计数器为0时代表锁没有被其线程所持有,那么任何线程都有其获取锁的机会
- 当一条线程获取到锁之后,JVM会记下锁的持有线程,将其计数器置为1
- 其他线程如果想要获取到锁,则必须要等待
- 改获取到锁的线程可以再次获取到锁,同时计数器自增1
- 当线程退出同步代码块时,计数器会递减,如果计数器为0,则释放锁
synchronized和ReentrantLock都是可重入锁