AQS是什么?
AQS全称是AbstractQueuedSynchronizer。
AQS实现树.png
AQS是实现显示锁,CountDownLatch,信号量以及线程池Worker里面的重要同步工具。
AQS里面使用了模版设计模式,对于开发者只需要实现某些步骤的方法就可以了。
模版方法.png
tryAcquire:排他锁,一般对数据的写都是使用排他锁
tryAcquireShare:共享锁,数据的读可以使用共享锁
AQS里面主要是通过修改一个state状态来完成锁操作的。当我们state=0的时候代表没有线程拿到该锁,当state不等于0的时候代表已经有其他线程拿到了锁。
使用AQS实现一个锁
public class MyLock implements Lock {
@Override
public void lock() {
synchronizer.acquire(1);
}
@Override
public void unlock() {
synchronizer.release(1);
}
@Override
public Condition newCondition() {
return synchronizer.newCondition();
}
private MySynchronizer synchronizer = new MySynchronizer();
static class MySynchronizer extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
Thread thread = Thread.currentThread();
//保证原子操作
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(thread);
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
throw new IllegalStateException("can not release lock");
}
//将持有锁的线程移除
setExclusiveOwnerThread(null);
setState(0);
return true;
}
Condition newCondition() {
return new ConditionObject();
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
synchronizer.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return synchronizer.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return synchronizer.tryAcquireNanos(1, unit.toNanos(time));
}
}
重入锁
重入锁:当某一个线程已经获取了该锁,还可以再次获取该锁
上面我们自定义的锁是不可重入的,当某个线程获取锁之后还想获取锁,由于state已经是1了,所以他就拿不到了,造成死锁了。
所以我们需要将这个线程获取锁的次数用state记录下来。
public class MyLock implements Lock {
@Override
public void lock() {
synchronizer.acquire(1);
}
@Override
public void unlock() {
synchronizer.release(1);
}
@Override
public Condition newCondition() {
return synchronizer.newCondition();
}
private MySynchronizer synchronizer = new MySynchronizer();
static class MySynchronizer extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
Thread thread = Thread.currentThread();
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(thread);
return true;
}
Thread ownerThread = getExclusiveOwnerThread();
if (ownerThread==thread){
setState(getState()+1);
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if(getExclusiveOwnerThread()!=Thread.currentThread()){
throw new IllegalMonitorStateException();
}
if (getState() == 0) {
throw new IllegalStateException("can not release lock");
}
setState(getState()-1);
if (getState()==0)
setExclusiveOwnerThread(null);
return true;
}
Condition newCondition() {
return new ConditionObject();
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
synchronizer.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return synchronizer.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return synchronizer.tryAcquireNanos(1, unit.toNanos(time));
}
}
AQS原理(双向链表)
1.除了修改state来记录锁的状态之外,
2.当这个锁已经被某个线程获取到了,其他线程也需要获取锁的时候,
3.其他线程先变成一个node节点,节点里面有当前线程的信息、上一个节点的信息以及是否需要获取锁的标记。
4.并且把当前节点放入队列锁的后面,
5.当前节点会一直自旋看看前面的节点是否已经释放锁,如果锁被释放,那就获取锁
6.如果前面节点一直没有释放锁,当前节点一直自旋会浪费cpu的性能,他自旋一定次数之后会被挂起
7.挂起之后会放入等待队列中,当被唤醒之后又会重新添加到队列锁上面
队列锁.png
公平锁和非公平锁
非公平锁:当某个线程获取锁的时候,先看看这个锁有没有被占用,如果被占用了就排在队列锁的后面
公平锁:当某个线程需要获取锁的时候,直接排在队列锁的后面。
网友评论