三,问题点
1(房间锁并发控制)
因为房间临时信息是存储在内存当中的并发容器的,所以并发容器的选择是一个性能方面的考量。
但是JAVA API 本身提供的并发容器table是一个总锁,所有的用户都需要排队,而另一种高效容器ConcurrentHashMap,因为读是无锁的,所以在读后进行业务逻辑再写入房间的时候并不适用,同时线性查找插入的方法也比较低效。
所以决定自己研发一个适用于解决“房间锁”问题的高效并发容器。开始自研“supermap”;
一开始准备的方案是适用二叉树的数据结构,以数组作为载体,通过移动树结构的下标来进行读写,同时每个节点都有一把锁,这样来保证一个房间一把锁,最多只有五个人共用一把锁。
但是后来发现这么做的两个问题,第一是数组容量不够,因为树结构每向下移动一层,数组就要以2^n进行扩容,而大量的数组内并没有实际值,容量不允许。
第二如果房间号出现连续递增 或者递减,树的平衡性无法保证,在最怀的情况下时间复杂度是N/2,所以要保证树结构的平衡性,充分利用每一个枝节,让空间复杂度稳定在log(N)之内。
转变思路,使用了红黑树作为平衡树结构的手段,而内容载体使用自定义的NODE,NODE单独享用一个读写锁,而不再使用数组来做。通过移动NODE的左右子树和旋转,变色等操作,维护树的平衡。当访问到相关节点时才调用节点拥有的读写锁。
但是此时又出现一个问题,我发现实现起来当调用一个节点的时候它相关节点 都会被锁住,这是递归函数的问题。
所以再次进行优化,在类里面我自建了一个HASH散列表,将每棵树的根节点,散列到HASH散列表中,这样在一个散列值中才享用一把读写锁,没个单独的节点又享用一把锁。
后来再实际情况中又发现了一个问题,因为对外提供的LOCKGET 和LOCKSET方法中,在实际运用中带锁读和带锁写,必须成对出现,如果只用了带锁读后面没有带锁写的话线程会阻塞。
因为在实际开发中有很多情况,导致程序并不能从头走到尾,完整下来,如果哪个环节漏写了带锁写,就会造成线程阻塞,这让调用人员必须非常细心在任何情况下都不能漏写。
在实际开发中,这样很耽误工夫。所以再次转变思路,对读写的控制将CAS中的无锁化设计融入其中。
设计了一套新的API,当用户读的时候 依然没有并发控制,但是结尾处的写入方法会感知前面的读操作有没有并发情况,如果有则返回TRUE 通知系统,进行线程回滚重新获取。
如果没有则直接进行下面的写入操作。
而且只有在同一个主键的情况下才会产生此写入并发通知。这样带锁写只是通知系统是否有并发读的情况。
整个容器完成,并经过测试,在同时有五千人并发写入房间信息的情况下,完成总时长平均200MS,吞吐TPS满足产品设计需求。
下面奉上源码请指教
public class SuperMap<K,V>{
//小小前进者 2018.10.12
//自创高并发容器SuperMap api 介绍
// SuperMAP<Integer,Object>,目前主键只支持整型数据
//get()//获取元素,非线程安全,执行获取没有并发控制
//put()//插入元素,线程安全返回布尔值,若布尔值为TURE 则代表着前面的get()有并发情况,禁止写入。
//并通知用户重新GET,并重新执行之后的业务线程 ,若为FALSE 则代表前面的GET 没有并发,进行数据写入,业务线程继续进行
//delete() 删除元素 线程安全
//put(int a)的并发控制 只跟SuperMap 的主键有关,当主键不一致的时候,put()并不会感知前面GET的并发情况。
//SuperMap基于自建HASH散列表分布根节点,根节点通过红黑树算法 查询插入PUT元素,数据结构为哈希散列表+红黑树结构
private static final boolean RED= true;
private static final boolean BLACK =false;
private static int size =0;
private static Object[] HashObject =new Object[19];
private Node<V>[] HashNode = new Node[19];
public SuperMap(){
for(int i=0;i<19;i++){
HashObject[i]=new Object();
}
}
private class Node<V>{
int key;
V val;
Node<V> left,right;
int N;
boolean color;
private ReentrantReadWriteLock rwl;
private AtomicReference<Integer> ato;
private int nownub ;
private boolean together =false;//是否处于并发状态
public boolean isTogether() {
return together;
}
public void setTogether(boolean together) {
this.together = together;
}
public void getNub(){
this.nownub = ato.get();
}
public boolean unlock(){
return this.ato.compareAndSet(this.nownub,this.nownub + 1);
}
Node(int key,V val,int N,boolean color){
this.key=key;
this.val=val;
this.N=N;
this.color=color;
this.rwl=new ReentrantReadWriteLock();
ato =new AtomicReference<Integer> ();
ato.set(0);
}
public ReentrantReadWriteLock getRwl() {
return rwl;
}
}
public boolean put(int key,V val){
int mc =key%19;
synchronized(HashObject[mc]){
Node<V> root = HashNode[mc];
root = put(root,key,val);
root.color =BLACK;
boolean bm = false;
bm =root.isTogether();
root.setTogether(false);
HashNode[mc]= root;
return bm;
}
}
public V get(int key){
V val =null;
Node<V> node ;
node = HashNode[key%19];
boolean bm =true;
do{
if(node!=null){
ReadLock lock = node.getRwl().readLock();
lock.lock();
int old = node.key;
if(old>key){
node=node.left;
}else if(old<key){
node =node.right;
}else{
//更新并发值
node.getNub();
bm =false;
val =node.val;
}
lock.unlock();
}else{
bm =false;
}
}while(bm);
return val;
}
public int size(){
return size;
}
public boolean delete(int key){
Node<V> node ;
node = HashNode[key%19];
boolean bm =true;
boolean isok =false;
do{
if(node!=null){
ReadLock lock = node.getRwl().readLock();
lock.lock();
int old = node.key;
if(old>key){
node=node.left;
lock.unlock();
}else if(old<key){
node =node.right;
lock.unlock();
}else{
bm =false;
lock.unlock();
//找到了要删除的节点,删除节点
delN(node);
isok =true;
}
}else{
bm =false;
}
}while(bm);
return isok;
}
private Node<V> delN(Node<V> h){
if(h==null){
return null;
}
WriteLock writelock= h.getRwl().writeLock();
writelock.lock();
if(h.left!=null){
exchange(h);//将左节点的值替换给自己
h =delN(h.left);
}else{
h =null;
}
writelock.unlock();
return h;
}
void exchange (Node<V> x){//跟他的左子树交换值
x.key =x.left.key;
x.val=x.left.val;
}
private Node<V> put(Node<V> h,int key,V val){
if(h==null){
return new Node<V>(key,val,1,RED);
}
int old = h.key;
if(key> old){
h.right=put(h.right,key,val);
if(h.right!=null&&h.right.isTogether()){//产生并发
h.right.setTogether(false);
h.setTogether(true);
}
}else if(key<old){
h.left =put(h.left,key,val);
if(h.left!=null&&h.left.isTogether()){//产生并发
h.left.setTogether(false);
h.setTogether(true);
}
}else{
if(h.unlock()){
h.setTogether(false);
h.val=val;
}else{//有读产生并发
h.setTogether(true);
System.out.println("该房间有读产生并发");
}
}
WriteLock lock= h.getRwl().writeLock();
lock.lock();
if(isRed(h.right)&&!isRed(h.left)){
h=rotateLeft(h);
}
if(isRed(h.left)&&isRed(h.left.left)){
h=rotateRight(h);
}
if(isRed(h.left)&& isRed(h.right)){
flipColors(h);
}
lock.unlock();
return h;
}
private boolean isRed(Node<V> x){
if(x ==null){
return false;
}
return x.color==RED;
}
void flipColors(Node<V> h){
h.color=RED;
h.left.color=BLACK;
h.right.color=BLACK;
}
Node<V> rotateLeft(Node<V> h){//
Node<V> x= h.right;
h.right =x.left;
x.left=h;
x.color = h.color;
h.color =RED;
x.N=h.N;
return x;
}
Node<V> rotateRight(Node<V> h){
Node<V> x= h.left;
h.left =x.right;
x.right=h;
x.color = h.color;
h.color =RED;
x.N=h.N;
return x;
}
}