All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.edgar615.util.concurrent.SequentialBlockingQueue Maven / Gradle / Ivy

The newest version!
package com.github.edgar615.util.concurrent;

import java.util.AbstractQueue;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 队列/消息是系统开发中经常使用的解耦利器。但是有一部分业务可能要对同一个用户/设备的消息按顺序处理. 基于这种需求,实现了动态路由队列,队列除了保存消息外,还保存了一个ID注册表,每次出队时,只能取出不在注册表中的消息,同时出队后需要在注册表中注册。
 * 在消息处理完成后,需要调用complete方法来实现清理注册表。
 *
 * @author Edgar  Date 2018/5/3
 */
public class SequentialBlockingQueue extends AbstractQueue implements BlockingQueue {

  /**
   * 任务列表
   */
  private final LinkedList elements = new LinkedList<>();

  /**
   * 消息注册表,用于确保同一个设备只有一个事件在执行
   */
  private final Set registry = new ConcurrentSkipListSet<>();

  private final IdentificationExtractor extractor;

  private final int limit;

  /**
   * Main lock guarding all access
   */
  private final ReentrantLock lock;

  /**
   * Condition for waiting takes
   */
  private final Condition waitingTake;

  /**
   * Condition for waiting puts
   */
  private final Condition waitingPut;

  private E takeElement;

  public SequentialBlockingQueue(IdentificationExtractor extractor, int limit) {
    this.extractor = extractor;
    this.limit = limit;
    this.lock = new ReentrantLock();
    this.waitingTake = lock.newCondition();
    this.waitingPut = lock.newCondition();
  }

  public SequentialBlockingQueue(IdentificationExtractor extractor) {
    this(extractor, Integer.MAX_VALUE);
  }

  public void complete(E task) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
      if (takeElement == null) {
        waitingTake.signalAll();
      }
      registry.remove(extractor.apply(task));
      takeElement = next();
    } finally {
      lock.unlock();
    }
  }

  @Override
  public void put(E e) throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
      //如果队满,阻塞入队
      while (this.elements.size() == this.limit) {
        waitingPut.await();
      }
      //唤醒等待出队的线程,如果队空或者下一个出队元素为null,说明可能会有出队线程在等待唤醒
      if (elements.isEmpty() || takeElement == null) {
        //唤醒出队
        waitingTake.signalAll();
      }
      //入队
      enqueue(e);
    } finally {
      lock.unlock();
    }

  }

  @Override
  public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
      //如果队满,阻塞入队
      while (this.elements.size() == this.limit) {
        if (nanos <= 0) {
          return false;
        }
        nanos = waitingPut.awaitNanos(nanos);
      }
      //唤醒等待出队的线程,如果队空或者下一个出队元素为null,说明可能会有出队线程在等待唤醒
      if (elements.isEmpty() || takeElement == null) {
        //唤醒出队
        waitingTake.signalAll();
      }
      enqueue(e);

    } finally {
      lock.unlock();
    }
    return false;
  }

  @Override
  public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
      //如果队列为空,或者下一个出队元素为null,阻塞出队
      while (elements.isEmpty() || takeElement == null) {
        waitingTake.await();
      }
      //唤醒等待入队的线程,如果队满,说明可能会有入队线程在等待唤醒(不满不会有入队线程等待唤醒)
      if (this.elements.size() == this.limit) {
        //唤醒入队,入队线程会在出队方法执行完毕并释放锁之后才开始抢占锁
        waitingPut.signalAll();
      }
      return dequeue();
    } finally {
      lock.unlock();
    }
  }

  @Override
  public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
      //如果队列为空,或者下一个出队元素为null,阻塞出队
      while (elements.isEmpty() || takeElement == null) {
        if (nanos < 0) {
          return null;
        }
        nanos = waitingTake.awaitNanos(nanos);
      }
      //唤醒等待入队的线程,如果队满,说明可能会有入队线程在等待唤醒(不满不会有入队线程等待唤醒)
      if (this.elements.size() == this.limit) {
        //唤醒入队,入队线程会在出队方法执行完毕并释放锁之后才开始抢占锁
        waitingPut.signalAll();
      }
      return dequeue();
    } finally {
      lock.unlock();
    }
  }

  @Override
  public int remainingCapacity() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
      return limit - elements.size();
    } finally {
      lock.unlock();
    }
  }

  @Override
  public int drainTo(Collection c) {
    throw new UnsupportedOperationException();
  }

  @Override
  public int drainTo(Collection c, int maxElements) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean offer(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
      if (elements.size() == limit) {
        return false;
      } else {
        enqueue(e);
        return true;
      }
    } finally {
      lock.unlock();
    }
  }

  @Override
  public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
      return (elements.size() == 0) ? null : dequeue();
    } finally {
      lock.unlock();
    }
  }

  @Override
  public E peek() {
    throw new UnsupportedOperationException();
  }

  @Override
  public Iterator iterator() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
      return new LinkedList<>(elements).iterator();
    } finally {
      lock.unlock();
    }
  }

  @Override
  public int size() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
      return elements.size();
    } finally {
      lock.unlock();
    }
  }

  private void enqueue(E e) {//入队
    elements.add(e);
    //计算下一个可以出队的元素
    if (!registry.contains(extractor.apply(e))
        && takeElement == null) {
      takeElement = e;
    }
  }

  private E dequeue() {//从队列中删除元素
    E x = takeElement;
    elements.remove(x);
    //将元素加入注册表
    registry.add(extractor.apply(x));
    //重新计算下一个可以出队的元素
    takeElement = next();
    return x;
  }

  private E next() {
    E next = null;
    for (E event : elements) {
      String id = extractor.apply(event);
      if (!registry.contains(id)) {
        next = event;
        break;
      }
    }
    return next;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy