Use and improvement of EvictingQueue in guava

1. Introduction

Because the business has some servers in foreign countries, the network is very unstable and fluctuates greatly when executing http requests. So we hope to switch to other servers to send http requests through http proxy when the network slows down.

What if the definition becomes slower?

If the http request has been executed for the last N times and the execution time exceeds the threshold T for times greater than or equal to M, it is considered that the current network is slow.

So we need to save the time of the last N execution of http requests. The first thing to determine is a FIFO queue, which needs a fixed size. In order to avoid re-creating wheels, we first decide to use guava's EvictingQueue.

Second, the basic use of EvictingQueue

@Test
public void testEvictingQueue(){
    
    
    EvictingQueue<Integer> queue = EvictingQueue.create(5);
    for (int i = 0; i < 10; i++) {
    
    
        queue.add(i);
        System.out.println(String.format("当前队列大小:%d,队列中元素:%s",queue.size(),StringUtils.join(queue.iterator(), ',')));
    }
}

result

EvictingQueue is created by create. The parameter is the maximum number of elements stored in the queue. After this number is exceeded, the first element added will be removed.

Three, the problem of EvictingQueue

The problem with EvictingQueue is that it is not thread-safe. You can see from the source code of EvictingQueue that it is actually a packaged ArrayDeque.

The concurrent access problem of EvictingQueue can be easily reproduced by the following code.

import com.google.common.collect.EvictingQueue;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;

import java.util.Iterator;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class EvictingQueueTest {
    
    

    @Test
    public void testEvictingQueue(){
    
    
        EvictingQueue<Integer> queue = EvictingQueue.create(5);
        for (int i = 0; i < 10; i++) {
    
    
            queue.add(i);
            System.out.println(String.format("当前队列大小:%d,队列中元素:%s",queue.size(),StringUtils.join(queue.iterator(), ',')));
        }
    }

    public static void main(String[] arg){
    
    
        EvictingQueue<Integer> queue = EvictingQueue.create(10);
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //十个生产线程不断向队列中写数据
        for(int i=0;i<10;i++){
    
    
            executorService.submit(new Producer(queue));
        }
        //一个生产线程不断去检测队列中满足条件的个数
        new Thread(new Consumer(queue)).start();
    }

    private static class Producer implements Runnable{
    
    

        private EvictingQueue<Integer> queue;

        public Producer(EvictingQueue<Integer> queue) {
    
    
            this.queue = queue;
        }

        @Override
        public void run() {
    
    
            Random random = new Random();
            while (true){
    
    
                queue.add(random.nextInt(100));
            }
        }
    }

    private static class Consumer implements Runnable{
    
    

        private EvictingQueue<Integer> queue;

        public Consumer(EvictingQueue<Integer> queue) {
    
    
            this.queue = queue;
        }

        @Override
        public void run() {
    
    
            while (true){
    
    
                int count = 0;
                Iterator<Integer> iterator = queue.iterator();
                while (iterator.hasNext()){
    
    
                    Integer integer = iterator.next();
                    if(integer < 50){
    
    
                        count++;
                    }
                }
                System.out.println("count:" + count);
            }
        }
    }
}

Four, EvictingQueue improvement plan

In order to deal with the concurrent access problem of EvictingQueue, we wrote a class to solve this problem.

Because it is mainly for statistical monitoring and does not require the data to be absolutely accurate, it does not use synchronization such as synchronize, and you can add it yourself.

import java.io.Serializable;
import java.util.function.Predicate;

public class EvictingArray<T> implements Serializable {
    
    

    private static final long serialVersionUID = 0L;

    private Object[] elements;

    private int index;

    private int capacity;

    private int size;

    public static EvictingArray create(int capacity){
    
    
        return new EvictingArray(capacity);
    }

    public EvictingArray(int capacity) {
    
    
        this.elements = new Object[capacity];
        this.capacity = capacity;
        this.size = 0;
        this.index = 0;
    }

    public void add(T element){
    
    
        elements[index++ % capacity] = element;
        if(size < capacity){
    
    
            size++;
        }
    }

    public void clear(){
    
    
//        Arrays.fill(elements,null);
        this.size = 0;
        this.index = 0;
    }

    //获取满足条件的元素个数
    public int getQualifiedNums(Predicate<T> predicate){
    
    
        int num = 0;
        for(Object ele : elements){
    
    
            if(predicate.test((T) ele)){
    
    
                num++;
            }
        }
        return num;
    }

    public int getSize() {
    
    
        return size;
    }
}

Test EvictingArray:


import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Predicate;

public class EvictingArrayTest {
    
    

    public static void main(String[] arg){
    
    
        EvictingArray<Integer> queue = EvictingArray.create(10);
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for(int i=0;i<10;i++){
    
    
            executorService.submit(new Producer(queue));
        }
        new Thread(new Consumer(queue)).start();
    }

    private static class Producer implements Runnable{
    
    

        private EvictingArray<Integer> array;

        public Producer(EvictingArray<Integer> array) {
    
    
            this.array = array;
        }

        @Override
        public void run() {
    
    
            Random random = new Random();
            while (true){
    
    
                array.add(random.nextInt(100));
            }
        }
    }

    private static class Consumer implements Runnable{
    
    

        private EvictingArray<Integer> array;

        public Consumer(EvictingArray<Integer> array) {
    
    
            this.array = array;
        }

        @Override
        public void run() {
    
    
            Predicate predicate = new Predicate<Integer>(){
    
    
                @Override
                public boolean test(Integer integer) {
    
    
                    return integer < 50;
                }
            };
            while (true){
    
    
                System.out.println("count:" + array.getQualifiedNums(predicate));
            }
        }
    }
}

Guess you like

Origin blog.csdn.net/trayvontang/article/details/105440147