设计一个 Map, 主线程高速插入和删除, 另一个线程定时删除 5 秒内没删除的数据

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 21:03   5803   0

看到一个面试题目:

设计一个 Map, 主线程高速插入和删除, 另一个线程定时删除 5 秒内没删除的数据

如果自旋 Map, 效率太低

引入 ConcurrentHashMap 和一个 Queue, 自旋遍历 Queue, 从头部开始移除

不知是否有更好的方法

/**
 * 设计一个 Map, 主线程高速插入和删除, 另一个线程定时删除 5 秒内没删除的数据
 * 高速: 想到 ConcurrentHashMap
 * 定时删除: 按时间顺序排列 -> 需要一个队列, 自旋遍历获取 key, 然后从 map remove, 
 * 直到队列里面值 插入时间 + 5秒 > 当前时间, 说明整个队列的值都还未过期
 */
public class QueueMap<K> {

 private volatile static QueueMap queueMap;

 /**
  * 双重锁单例
  */
 public static QueueMap instanceDouble() {
  if (queueMap == null) {
   synchronized (QueueMap.class) {
    if (queueMap == null) {
     queueMap = new QueueMap();
    }
   }
  }
  return queueMap;
 }

 /**
  * 静态内部类单例
  */
 public static QueueMap staticInner() {
  return QueueMapHolder.INSTANCE;
 }
 public static class QueueMapHolder {
  private static final QueueMap INSTANCE = new QueueMap();
 }

 private QueueMap() {
 }

 public static class SomeValue {
  private String key;
  private long millTime;

  public SomeValue(String key, long millTime) {
   this.key = key;
   this.millTime = millTime;
  }

  public String getKey() {
   return key;
  }

  public void setKey(String key) {
   this.key = key;
  }

  public long getMillTime() {
   return millTime;
  }

  public void setMillTime(long millTime) {
   this.millTime = millTime;
  }

 }
 /**
  * 实际操作的 get put remove
  */
 private Map<K, SomeValue> map = new ConcurrentHashMap<>();

 /**
  * 队列存数据, V 里面需要有时间戳信息
  */
 private Queue<SomeValue> queue = new ConcurrentLinkedQueue<>();

 private static final int EXPIRE_MILL = 5000;

 public void init() {
  System.out.println("子线程外面");
  new Thread(() -> {
   while (true) {
    SomeValue head = queue.peek();
    // 头的没过期, 后面的更不会过期
    if (head == null || head.getMillTime() + EXPIRE_MILL > System.currentTimeMillis()) {
     continue;
    }
    map.remove(head.getKey());
    SomeValue poll = queue.poll();
    System.out.println("线程" + Thread.currentThread().getName() + "删除 key " + poll.getKey());
   }
  }).start();
 }

 public void put(K k, SomeValue someValue) {
  SomeValue oldValue = map.put(k, someValue);
  // 从队列里面移除旧的数据
  if (oldValue != null) {
   queue.remove(oldValue);
  }
  queue.add(someValue);
 }

 public SomeValue get(K k) {
  SomeValue someValue = map.get(k);
  // 判断是否过期
  if (someValue != null && someValue.getMillTime() + EXPIRE_MILL >= System.currentTimeMillis()) {
   return someValue;
  }
  return null;
 }
 public void remove(K k) {
  map.remove(k);
 }

 public static void main(String[] args) throws InterruptedException {
  QueueMap doubleInstance = QueueMap.instanceDouble();
  doubleInstance.init();
  TimeUnit.SECONDS.sleep(3);
  QueueMap staticInnerInstance = QueueMap.staticInner();

  for (int i = 0; i < 10; i++) {
   if (i % 5 == 0) {
    doubleInstance.remove("key" + (i - 1));
   }
   doubleInstance.put("key" + i, new QueueMap.SomeValue("key=" + i, System.currentTimeMillis()));
  }

  System.out.println(111);
 }
}

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP