Back to Home

Java round-robin thread-safe implementation

java · round-robin · thread save · balancer

Java round-robin thread-safe implementation

Good day, dear habrachitatel.
One fine day, the authorities set the task of adding the ability to use several servers to improve productivity in our system. It was not possible to expand the cluster and balance through specialized means. Therefore, I had to invent my own implementation using the round-robin algorithm.

Algorithm


Based on the architecture of the application, it was decided that balancing should be done by going through the list of servers that are specified in the configuration and issuing the address of the next server on a new request.

This principle underlies the round-robin algorithm.

Implementation


After searching the Internet, I found many options for implementing this algorithm, which boiled down to the following code:
    public synchronized Entity getNext_Iterator(){
        if (!it.hasNext()) {
            it = objectList.iterator();
        }
        return it.next();
    }


Here objectList is a list of available servers.

In this implementation, I did not like the use of the synchronized method, so I proposed the following implementation:
  1. During initialization, a list of available servers is created and the length of this list is preserved. A global counter is created with the AtomicInteger data type and an initial value of 0
  2. When requesting the server address, the counter increases by 1. If the resulting value exceeds the length of the list, then zero the counter. We use the value of this counter as an index in the list.


Now directly the resulting code (thanks for the relgames advice ):

    private final AtomicInteger position = new AtomicInteger(0);
    private volatile int listSize;
    ...
    public synchronized void init(List objects) {
            ...
            listSize = objectList.size();
            ...
        }
    }
    public Entity getNext() {
         return objectList.get(getNextPosition());
    }
    public final int getNextPosition() {
        for (;;) {
            int current = position.get();
            int next = current + 1;
            if(next >= listSize){
                next = 0;
            }
            if (position.compareAndSet(current, next))
                return current;
        }
    }


To test the health of this implementation, a small program was written under load.
The testing methodology consisted in the fact that 1,000,000 requests were made to obtain the server address with a different number of threads (from 1 to 991) and the average time to obtain the address was measured.

The results are presented in a graph and can be viewed here .
Sources for experiments here .
The data on which the graphs are based is here .

Thanks for attention.

PS The proposed implementation option works equally stable both when working in a single-threaded system, and when working in a multi-threaded system, which is clearly visible on the graph. This is the main advantage of this implementation.

UPD: The
old schedule is here . Research was then conducted on a slower computer.

Read Next