这是阿里一面的一道设计题,限制一个用户一秒只能访问100次。
isAllow来判断用户是否可以访问(超过频率限制就不行)
思路是一个100容量的容器记录每一次访问的时间,只要没有一百次就是可以访问。
超过一百次则统计第一个和第101个的时间差是非小于1秒,小于一秒超过频率限制,返回false
没超过就删除第一个加入最新的一个返回true,保证每次统计的都是最新的100次访问就行。
原本的第一版实现使用的是CopyOnWriteArrayList<Long>?后面改为为PriorityBlockingQueue<long>;
原因是CopyOnWriteArrayList<Long>?记录的时间在多线下记录的顺序未必是时间升序(第一个未必是最早访问的时间),PriorityBlockingQueue<long>的peek每次都会返回队列中时间最小的元素(最早访问的时间)这样就能确保统计的是正确的时间。
``` java?
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.locks.ReentrantLock;
public class Solution56 {
static ConcurrentHashMap<String, PriorityBlockingQueue<Long>> count = new ConcurrentHashMap<String, PriorityBlockingQueue<Long>>();
private final ReentrantLock lock = new ReentrantLock();
boolean isAllow(String clientId) {
lock.lock();
try {
while (count.get(clientId) == null) {
count.put(clientId, new PriorityBlockingQueue<Long>());
}
} finally {
lock.unlock();
// TODO: handle finally clause
}
long currentTimeMillis = System.currentTimeMillis();
PriorityBlockingQueue<Long> priorityQueue = count.get(clientId);
if (priorityQueue.size() >= 100) {
if (currentTimeMillis - priorityQueue.peek() <= 1000) {
priorityQueue.offer(currentTimeMillis);
if (priorityQueue.size() > 100) {
priorityQueue.poll();
}
return false;// 一百次少于一秒
} else {
priorityQueue.offer(currentTimeMillis);
if (priorityQueue.size() > 100) {
priorityQueue.poll();
}
// 一百次多于一秒删除第一个重新统计
return true;
}
}
priorityQueue.offer(currentTimeMillis);
return true;
}
public static void main(String[] args) {
Solution56 solution56 = new Solution56();
List<Thread> list = new ArrayList<Thread>();
for (int i = 0; i < 101; i++) {
Thread thread = new Thread(() -> {
if (!solution56.isAllow("yzq")) {
System.out.println(false);
}
});
list.add(thread);
thread.start();
}
}
}
```?