文章目录
AQS 原理
FIFO( First Input First Output)简单说就是指先进先出
AQS(AbstractQueuedSynchronizer类)是一个用来构建锁和同步器的框架,各种Lock包中的锁(常用的有Reentrantlock、ReadWritelock),都是基于AQS来构建。
-
1.AQS在内部定义了一个volatile int state变量,表示同步状态:当线程调用lock方法时,如果state=0,说明没有任何线程占有共享资源的锁,可以获得锁并将state=1;如果state=1,则说明有线程目前正在使用共享变量,其他线程必须加入同步队列进行等待。
-
2.AQS通过Node内部类构成的一个双向链表结构的同步队列,类似Monitor的EntryList,来完成线程获取锁的排队工作,当有线程获取锁失败后,就被添加到队列未尾。
Monitor是C++实现的,AQS纯java实现的
简单的不可重入锁的实现:
测试:
ReentrantLock原理
非公平锁实现原理
加锁成功:
加锁失败:
尾插法进入阻塞队列
解锁:
加锁失败:
如果加锁失败头结点是不会断开的,所以还在第一位
公平锁实现原理
非公平锁实现:
- 获取之前不会去检查AQS队列
公平锁实现原理: - 获取之前会检查队列中是否有第二个节点,或者自己是不是第二个节点,如果没有节点或者自己是第二个节点才会去竞争锁
可重入原理
加锁:
本质为判断如果是当前线程则 state+1
解锁:
解锁时state-1 如果为0才解锁
条件变量原理
Condition和Lock一样,也是JUC内的一个接口。Condition接口定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到 Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,换句话说,Condition是依赖Lock对象的。
可以通过:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
新建条件变量,一个lock可以存在多个条件变量
线程等待让出资源进入条件变量阻塞:持有lock的线程调用await()
线程唤醒:持有lock的线程signal()
唤醒所有线程:持有lock的线程signalAll()
底层调用的是:park unpark
简单使用:
package com.zwx.concurrent.lock;
import java.util.Locale;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockConditionDemo {
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(new ConditionAwait(lock,condition)).start();
Thread.sleep(1000);
new Thread(new ConditionSingal(lock,condition)).start();
}
}
class ConditionAwait implements Runnable{
private Lock lock;
private Condition condition;
public ConditionAwait(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("await begin");
try {
lock.lock();
condition.await();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
System.out.println("await end");
}
}
class ConditionSingal implements Runnable{
private Lock lock;
private Condition condition;
public ConditionSingal(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("signal begin");
try {
lock.lock();
condition.signal();
}finally {
lock.unlock();
}
System.out.println("signal end");
}
}
原理解析:
await:
必须获得锁之后才能await()
signal原理
必须获得锁的线程才能使用signal()否则会抛出异常
唤醒单个:
唤醒所有:
循环将首节点依次插入阻塞队列尾部
尝试获取锁的过程中 可打断的原理
两个不同的加锁方法会有不一样的效果
1.不可打断模式 lock()
- 线程被打断首先会清除自己的打断标记,因为未清除打断标记则下一次park无法成功。
- acquireQueued()只是记录了打断标记,没有做任务处理,只有获取到锁之后线程才能知道自己被打断了,重新产生一次中断。
2.可打断模式 lockInterrruptibly()
- 可打断模式下 打断之后不管有没有获得锁都会立刻就抛出了异常