关于在开发棋牌手游,遇到的房间局部锁问题与房间ID问题及解决方案

三,问题点
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;
   }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值