对象分配也有乐观和悲观

   最近做公司的网游项目,使用JAVA开发,网络通讯这块使用的则是mina框架。说实话,到现在也只是知道怎么用,内部实现还没仔细研究过。跟成熟的框架比起来,感觉自己之前在.NET上写的Socket在结构化程度上稍差了点,作为一名C# er,便有了模仿其API完善.NET类库的想法。这其中涉及很多技术细节,如线程同步、异步通讯、TCP协议解析、对象池使用及管理等。今天要讲的主题便是这个对象池了。
   之所以要用到对象池,是因为在一个典型的TCP/IP应用中,在服务器运行期间,会有无数次收到消息,格式化成指定对象(这里暂时叫Packet吧),并且服务器处理后组织特定的Packet返回给客户端。这样如果每次都用new操作来创建Packet对象的话,加上对象的GC处理回收时间,会对性能产生一定影响。如果服务器压力不大的话,确实可以不用对象池,这样可以保持代码的简洁。不过我的看法是,即使暂时你的服务器压力瓶颈不在这,我们也可以先在底层提供这样的功能,作为技术积累,搞程序还是有点适当的前瞻意识比较好。
   对象池,最简单的功能当然就是实现对象复用,避免反复创建及销毁对象。查阅了一些文章,个人总结主要的区别集中在管理策略及管理途径上。策略这东西,可以很简单,可以很复杂,因情况而异。比如.NET中的线程池中关于线程的管理策略,可细分为繁忙时的创建策略及空闲时的销毁。我们的目标是实现一个尽量通用的对象池,因此,用到的管理策略应该是尽量的简单。
   再说说管理途径,一般有两种方式:
     1、通过封装对象并附加一些池管理需要参数,如是否空闲,最后一次使用时间等,可以称之为ObjectWrapper;
     2、通过让对象实现特定接口来实现复用,如实现IDisposable接口或自己另外定义接口。
 以上两种方法各有利弊,第一种可以额外附加不同种类的参数,达到辅助管理的目的,第二种可能功能稍微单一,但是贵在简洁,这里我选的是后者,并让池化对象实现一个IPoolable接口,同时定义对象池对象接口IObjectPool,代码如下

 

    public   interface  IPoolable
    {
        
///   <summary>
        
///  将对象释放回对象池,务必确保事件等关联资源先清除
        
///   </summary>
         void  Free();

        
///   <summary>
        
///  管理自身的对象池的引用
        
///   </summary>
        IObjectPool < IPoolable >  Pool {  get set ; }
    }

    
public   interface  IObjectPool < T >   where  T : IPoolable
    {
        
///   <summary>
        
///  获取空闲对象
        
///   </summary>
        T GetFreeObject();

        
///   <summary>
        
///  释放对象回对象池
        
///   </summary>
         void  ReleaseObject(T item);
    }

 

 

Free方法要求池化对象的实现者将自身返回给对象池的空闲列表,这样做的好处在于对象的使用者可以不用关注对象池的细节。

   对象的管理这一问题,确实伤了不少脑筋。使用动态管理,我认为太过复杂,如果这么做,至少要做到可销毁对象的定义,怎么样才算可销毁,是对象空闲了还是一定时间没用了,还是当前数量超出了某个值等。上层应用使用不同对象的情况各有差异,如果简单的用一个标准定死,很难通用。
   另一种比较常见的方式,我称之为“乐观获取”,这种做法在无可用对象,并且对象总数达到数量上限时,会假定所有对象的使用者会及时的返回对象,此时请求者选择Sleep或者WaitOne等方式等待。我认为这是种雪上加霜的解决办法,当所有可用对象已经被使用,并且达到了数量上限,这时候认为系达到对象使用的卖方市场,即供不应求。即使有限的对象被返回,也不能满足更多的需求,最坏的情况是一些请求者一直等待。
   基于此,我选择了“悲观获取”的方式,这种做法在请求对象时,保证返回一个正确对象,虽然这有可能造成系统中对象总数暂时超

出了数量限制,至少可以保证不会因为没可用对象而使上层逻辑受阻。而当系统压力减轻时,可通过控制返回对象的逻辑来销毁多余的对象。

对象池代码如下:

 
 

代码
     ///   <summary>
    
///  无需等待的对象池
    
///   </summary>
     class  UnWaitObjectPool < T >  : IObjectPool<T> where  T : IPoolable,  new ()
    {
        
private   int  _minObjCount;
        
private   int  _maxObjCount;
        
private   object  _mutex;
        
private  Queue < T >  _freeQueue; // 空闲队列

        
private  UnWaitObjectPool( int  min,  int  max)
        {
            
if  (min  >  max  ||  min  <   0   ||  max  <   0 )
            {
                
throw   new  ArgumentException( " illegal args. " );
            }

            
this ._minObjCount  =  min;
            
this ._maxObjCount  =  max;
            
this ._mutex  =   new   object ();
            
this ._freeQueue  =   new  Queue < T > ();

            
for  ( int  i  =   0 ; i  <  min; i ++ )
            {
                
this ._freeQueue.Enqueue( new  T());
            }
        }

        
///   <summary>
        
///  有空闲对象则直接取出,如果没有,直接创建,这里无视数量上限
        
///   </summary>
        
///   <returns></returns>
         public  T GetFreeObject()
        {
            
if  ( this ._freeQueue.Count  >   0 )
            {
                
lock  ( this ._mutex)
                {
                    
if  ( this ._freeQueue.Count  >   0 )
                    {
                        
return   this ._freeQueue.Dequeue();
                    }
                }
            }

            
return   new  T();
        }

        
///   <summary>
        
///  返回对象,每次调用后如果空闲对象超出限制,则销毁
        
///   </summary>
        
///   <param name="item"></param>
         public   void  ReleaseObject(T item)
        {
            
lock  ( this ._mutex)
            {
                
this ._freeQueue.Enqueue(item);
            }

            
this .TryTrimToMax();
        }

        
private   void  TryTrimToMax()
        {
            
while  ( this ._freeQueue.Count  >   this ._maxObjCount)
            {
                
this ._freeQueue.Dequeue();
            }
        }
    }

 

 

是不是简单很多?这里少了不必要的WaitOne等线程同步操作,以及复杂的管理策略。通俗的说,这里也许跟传统概念的对象池不太一样,因为没有严格控制对象的创建,因此算是一种折中的方案,当对象需求紧张时,对象池与普通new方法一起发挥作用,两种机制同时服务于上层应用。而当系统压力一般时,对象池独自承担对象的维护。
   这里只给出部分代码,忽略了单例的部分,对象池对象本身是单例的!但是我们的目的是作为简单或者默认的对象池,前文提到的Packet只是一种应用场景,既然设计成泛型,肯定是希望尽量的通用,因此如果有遗漏的地方没想到,欢迎大家批评及拍砖。

 

 参考文章:
 Java对象池技术的原理及其实现http://www.cnblogs.com/aurawing/articles/1887029.html
 使用.net技术创建一个小型对象池http://www.cnblogs.com/chegan/archive/2004/03/04/2129.html
 SocketAsyncEventArgs对象池示例http://msdn.microsoft.com/en-us/library/bb551675.aspx
 数组缓存池示例http://msdn.microsoft.com/en-us/library/bb517542.aspx

你可能感兴趣的:(对象)