并发必须详解的AQS
以java为例子,谈到并发问题,最关键的两个synchronized(下篇文章会谈谈的)和ReentrantLock,而谈到ReentrantLock,不得不谈的AbstractQueuedSynchronizer(AQS)!
首先将AQS应用于Lock中人叫Doug Lea,被称为并发之父,网传:生平不识Doug Lea,学懂并发也枉然。
java并发编程核心包在于java.concurrent.util包而JUC当中的大多数同步器实现都是围绕着共同的基础行为,比如等待队列,条件队列,独占获取,共享获取等,concurrentHashMap,ConcurrentLinkedQueue等等都在这个下面。它们这些行为的抽象就是基于AQS的,
ReentrantLock
ReentrantLock是一种具有AQS框架的应用实现,是jdk中一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全。而且它具有比synchronized更多的特性,如:手动加锁与解锁,处理公平和非公平性问题。(与synchronized区别会在下篇synchronized中谈到)
三大核心原理
自旋锁,LocksSuport,CAS,queue队(双向循环链表)
自旋锁
轻量级锁失败后,虚拟机为了避免线程真实地在操作系统层面挂起,还会进行一项称为自旋锁地优化手段。在大多数情况下,线程持有锁地时间都不会太长,如果直接挂起os层面地线程可能会得不偿失,因为挂起动作,在os层面会做用户态转换到内核态,这种切换时间长,成本高,因此自旋锁,会假设在不久将来,当前地线程可以获得锁,因此虚拟机会让当前想要获取锁地线程做几个空循环,一般不会太久(50-100次),在经过若干循环后,得不到锁,才会进行os层面地挂起,将其升级为重量级锁。
LocksSuport
juc下面地提供了类,包含park()加锁,unpark()解锁,其实往底层看看它最终实现是非java语言实现地代码。
CAS
Compare And Swap --乐观锁策略
顾名思义:比较交换来判断是否出现冲突,出现冲突就重试直到不冲突为止。
假设所有线程访问共享资源时不会出现冲突,既然不会出现冲突自然就不会阻塞其他线程。线程不会出现阻塞状态。
反之还有个叫悲观锁(JDK1.6之前的内建锁):假设每一次执行同步代码块均会产生冲突,所以当线程获取锁成功,会阻塞其他尝试获取该锁的线程。
其实很简单,如图:
queue队(双向循环链表)
这就是一个双向循环链表 ,这个链表中会有n个节点,每个节点是由主要的三部分组成pre,next,data。其实data会因系统而异,也会由多部分组成。pre指向前驱节点,next指向后继节点。
如:我们接下来要讲的ReentrantLock中就用到过
java.util.concurrent.locks.Node类中
上面是链表中一个节点,组成后形成全的链表。
接下通过java代码实现双向循环链表的增、删、查操作。
下面展示一些 内联代码片
。
public class DoubleLink<Data> {
Node<Data> head;//头节点
Node<Data> end; //尾节点
int size;//链表长度
//初始化链表
public void initList() {
end = new Node<>(null,null,null);
head = new Node<>(null,null, end);
end.prev = head;
end.next = head;
size =0;
}
//获取长度
public int length() {
return size;
}
//获取节点
//在查找时为了优化查找速度,借用了折半查找的思想进行查找
public Node<Data> getNode (int index) {
Node<Data> n;
if(index>size/2){
n = end;
for (int i = length(); i > index; i--) {
n = n.prev;
}
return n;
} else {
n=head;
for (int i =0 ;i < index; i++) {
n = n.next;
}
return n;
}
}
//添加节点(尾插)
public void addNode (Data data) {
Node<Data> renode = new Node<>(data,getNode(size-1), end);
renode.prev.next=renode;
renode.next.prev=renode;
size++;
}
//指定位置插入节点
public void addNodeByIndex (int i, Data data) {
Node<Data> n=getNode(i);
Node<Data> renode=new Node<>(data,n.prev,n);
n.prev.next=renode;
n.prev=renode;
size++;
}
//删除元素
public Data remove(int i){
Node<Data> n=getNode(i);
Data data=n.data;
n.prev.next=n.next;
n.next.prev=n.prev;
size--;
return data;
}
}