Zoie源代码阅读----AsyncDataConsumer

      这个类的关于consume() 和 flushBuffer()方法的等待调用比较绕逻辑 。

package proj.zoie.impl.indexing;

import java.util.Collection;
import java.util.LinkedList;

import org.apache.log4j.Logger;

import proj.zoie.api.DataConsumer;
import proj.zoie.api.ZoieException;
/**
 * 数据的消费者,由ZoieSystem来继承,实际的的数据消费者 ,由StreamDataProvider.start()方法启动的线程DataThread来调用
 * 其中也有一个DataConsumer<V> _consumer,
 * @author cctv
 * @param <V>
 */
public class AsyncDataConsumer<V> implements DataConsumer<V>
{
  private static final Logger log = Logger.getLogger(AsyncDataConsumer.class);
  
  private ConsumerThread _consumerThread;
  private DataConsumer<V> _consumer;
  private long _currentVersion;
  private long _bufferedVersion;
  private LinkedList<DataEvent<V>> _batch;
  private int _batchSize;

  public AsyncDataConsumer()
  {
    _currentVersion = -1L;
    _bufferedVersion = -1L;
    _batch = new LinkedList<DataEvent<V>>();
    _batchSize = 1; // default
    _consumerThread = null;
  }
  //如果这个方法不调用,则不能启动消费的进程_consumerThread 。
  public void start()
  {
    _consumerThread = new ConsumerThread();
    _consumerThread.setDaemon(true);
    _consumerThread.start();
  }
  
  public void stop()
  {
    _consumerThread.terminate();
  }
  
  public void setDataConsumer(DataConsumer<V> consumer)
  {
    synchronized(this)
    {
      _consumer = consumer;
    }
  }
  
  public void setBatchSize(int batchSize)
  {
    synchronized(this)
    {
      _batchSize = Math.max(1, batchSize);
    }
  }
  
  public int getBatchSize()
  {
    synchronized(this)
    {
      return _batchSize;
    }
  }
  
  public int getCurrentBatchSize()
  {
    synchronized(this)
    {
      return (_batch != null ? _batch.size() : 0);
    }
  }
  
  public long getCurrentVersion()
  {
    synchronized(this)
    {
      return _currentVersion;
    }
  }
  
  public void flushEvents(long timeout) throws ZoieException
  {
    syncWthVersion(timeout, _bufferedVersion);
  }
  
  public void syncWthVersion(long timeInMillis, long version) throws ZoieException
  {
    long now = System.currentTimeMillis();
    long due = now + timeInMillis;
    
    if(_consumerThread == null) throw new ZoieException("not running");
    
    synchronized(this)
    {
      while(_currentVersion < version)
      {
        if(now >= due)
        {
          throw new ZoieException("sync timed out");
        }
        try
        {
          this.wait(due - now);
        }
        catch(InterruptedException e)
        {
          log.warn(e.getMessage(), e);
        }
        now = System.currentTimeMillis();
      }
    }
  }
  /**
   * 别的对象调用此方法将要消费的数据传递进来 。
   * 该方法会判断LinkedList<DataEvent<V>> _batch中是否装满数据,若是装满,则休眠这个方法,然后会唤醒flushBuffer()去将_batch中数据消费完
   */
  @Override
  public void consume(Collection<DataEvent<V>> data) throws ZoieException
  {
    if (data == null || data.size() == 0) return;
    
    synchronized(this)
    {
      while(_batch.size() >= _batchSize)  //如果数据满了,则休眠自己一会让其他的方法(flushBuffer())去flush掉数据到真正的ZoieSystem中,等使用完数据该方法会继续执行下去
      {
        if(_consumerThread == null || !_consumerThread.isAlive() || _consumerThread._stop)
        {
          throw new ZoieException("consumer thread has stopped");
        }
        try
        {
          this.wait();
        }
        catch (InterruptedException e)
        {
        }
      }
      for(DataEvent<V> event : data)
      {
        _bufferedVersion = Math.max(_bufferedVersion, event.getVersion());
        _batch.add(event);
      }
      this.notifyAll(); // wake up the thread waiting in flushBuffer()
    }
  }
  //该对象启动时自动创建一个守护进程,然后不停的来掉这个方法
  /**
   * 该方法会不停的被一个守护进程ConsumerThread来调用,每次取"吃掉"外界传来的在LinkedList<DataEvent<V>> _batch中存的数据,
   * 若是调用时_batch中没有数据,则等待该方法,唤醒consume()去装数据,装完一次数据后在唤醒自己,去“吃掉”这部分数据
   */
  protected final void flushBuffer()
  {
    long version;
    LinkedList<DataEvent<V>> currentBatch;
    
    synchronized(this)
    {
      while(_batch.size() == 0)    //如果等于0的话,则休眠自己,让别的线程(consume())使用,去装数据 ,装完数据会后会继续往下执行
      {
        if(_consumerThread._stop) return;
        try
        {
          this.wait();
        }
        catch (InterruptedException e)
        {
        }
      }
      version = Math.max(_currentVersion, _bufferedVersion);
      currentBatch = _batch;
      _batch = new LinkedList<DataEvent<V>>();
      
      this.notifyAll(); // wake up the thread waiting in addEventToBuffer()
    }
    //消费数据
    if(_consumer != null)
    {
      try
      {
        _consumer.consume(currentBatch);
      }
      catch (Exception e)
      {
        log.error(e.getMessage(), e);
      }
    }
    
    synchronized(this)
    {
      _currentVersion = version;
      this.notifyAll(); // wake up the thread waiting in syncWthVersion()
    }    
  }
//内启动的线程,不停的调用父对象的flushBuffer()
  private final class ConsumerThread extends IndexingThread
  {
    boolean _stop = false;
    
    ConsumerThread()
    {
      super("ConsumerThread");
    }
    
    public void terminate()
    {
      _stop = true;
      synchronized(AsyncDataConsumer.this)
      {
        AsyncDataConsumer.this.notifyAll();
      }
    }
    
    public void run()
    {
      while(!_stop)
      {
        flushBuffer();
      }
    }
  }
}

该类数据实际做索引的对象的一个数据中专站的形式,存储数据并中转,自己并不实际消费数据 。

posted @ 2011-06-24 18:23  xiao晓  阅读(415)  评论(0编辑  收藏  举报