Okio框架分析--之二

4.2 SegmentPool

SegmentPool就是片段池,可以说是管理Segment。三个变量如下,

static final long MAX_SIZE = 64 * 1024; // 64 KiB.
static Segment next;
static long byteCount;

MAX_SIZE表示片段池的最大容量,表示片端池中最多可以容纳8个片段,next将片段连接成单向链表;

byteCount表示片端池当前包含的总字节数。

take方法如下,

static Segment take() {
    synchronized (SegmentPool.class) {
      if (next != null) {
        Segment result = next;
        next = result.next;
        result.next = null;
        byteCount -= Segment.SIZE;
        return result;
      }
    }
   return new Segment(); // Pool is empty. Don't zero-fill while holding a lock.
}

take方法用于从片段池中取片段,当next为空即片段池为空时,新建一个片段返回(此新建片段在用完后回收进片段池),

若片段池不为空则返回池中第一个片段,并将这个片段从片段池中移除,其中加锁防止多线程同时取数据。

recycle方法如下,

static void recycle(Segment segment) {
    if (segment.next != null || segment.prev != null) throw new IllegalArgumentException();
    if (segment.shared) return; // This segment cannot be recycled.
    synchronized (SegmentPool.class) {
      if (byteCount + Segment.SIZE > MAX_SIZE) return; // Pool is full.
      byteCount += Segment.SIZE;
      segment.next = next;
      segment.pos = segment.limit = 0;
      next = segment;
    }
}

recycle方法用于将不用的Segment回收至片段池,且首先要判断即将回收的Segment的next和prev是否已经置空,

即是否已经从Segment双向链表中正确的移除,然后还要判断此Segment是否为共享状态,为共享状态表示此Segment

可能还要执行一些操作,比如正在移动Segment,此时也不能进行回收,最后一层判断片段池能否容纳此Segment。

符合以上三个条件后进行回收,将此Segment参数初始化,加入单向链表,等待被取出使用。

4.3 write

下面通过Buffer的write方法分析Buffer是如何通过Segment和SegmentPool实现高效性的

Buffer有一个write方法如下,

public void write(Buffer source, long byteCount) {
if (source == null) throw new IllegalArgumentException("source == null");
    if (source == this) throw new IllegalArgumentException("source == this");
    checkOffsetAndCount(source.size, 0, byteCount); //判断参数的合理性
while (byteCount > 0) {
      // Is a prefix of the source's head segment all that we need to move?
       // 判断要移动的数据是否全部存储在source的第一个片段中
      if (byteCount < (source.head.limit - source.head.pos)) {
        Segment tail = head != null ? head.prev : null;
        if (tail != null && tail.owner
            && (byteCount + tail.limit - (tail.shared ? 0 : tail.pos) <= Segment.SIZE)) {
          // Our existing segments are sufficient. Move bytes from source's head to our tail.
           //若目标片段tail可用且容量足以存储数据
          // 将source头片段中的数据移动到目标片段tail
source.head.writeTo(tail, (int) byteCount);
          source.size -= byteCount;
          size += byteCount;
          return;
        } else {
          // We're going to need another segment. Split the source's head
          // segment in two, then move the first of those two to this buffer.
//若目标片段tail不可用或容量不足以存储数据
          //使用split方法分割source片段,并将分割得到的待写入数据片段作为source的第一个片段
          source.head = source.head.split((int) byteCount);
        }
      }

      // Remove the source's head segment and append it to our tail.
// 将source第一个片段移动到目标片段
      Segment segmentToMove = source.head;
//移除source第一个片段,并将source的后置节点前移作为source第一个片段,相当于堆栈的出栈
      long movedByteCount = segmentToMove.limit - segmentToMove.pos;
      source.head = segmentToMove.pop();
      if (head == null) {
//若待写入Buffer(即this)中没有任何片段,则直接将头片段指向数据片段
        head = segmentToMove;
        head.next = head.prev = head;
      } else {
        //否则将数据片段移动到头片段的前置片段,并尝试压缩
        Segment tail = head.prev;
        tail = tail.push(segmentToMove);
        tail.compact();
      }
      //重置容量,若没有移动完则循环
      source.size -= movedByteCount;
      size += movedByteCount;
      byteCount -= movedByteCount;
    }
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值