Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Maxed CPU Waiting on non-blocking queue

I have the following code:

public void run() {
    while (true) {
        m = q.poll();
        if (m != null) {
            kf.sendMessage(m.topic, m.message);
        }
    }
}

where q is a ConcurrentLinkedQueue. Currently this is eating 100% of my CPU. Is there a more efficient way to wait on a non-blocking queue? I prefer to use a non-blocking queue because I am expecting bursty traffic from the producers on the queue, so I want to maximize performance. Is there a way to relinquish control of the cpu for my thread if q.poll() returns null?

I have the option of switching to a blocking queue, but I am curious what the proper way of doing this is.

Edit - Lots of good responses! Thanks for all your help. For now I'm going to just switch to a linkedblockqueue, and if I start running into performance issues re-evaluate.

like image 649
Trevor Avatar asked Oct 01 '22 22:10

Trevor


2 Answers

If you have nothing else to do, and just want to wait (without using CPU) until data is available, then use a blocking queue. That's exactly what it's made for, with methods like take:

Retrieves and removes the head of the queue represented by this deque (in other words, the first element of this deque), waiting if necessary until an element becomes available.

If you are interesting in how this is implemented, you can take a look at the source for these classes, for example LinkedBlockingQueue#take:

public E take() throws InterruptedException {
    E x;
    int c = -1;
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        while (count.get() == 0) {
            notEmpty.await();
        }
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    return x;
}

As you can see, they maintain a couple of Conditions to signal if the queue is empty or not.

like image 106
Thilo Avatar answered Oct 04 '22 07:10

Thilo


Since you're dealing with bursty behavior, you could wait when you've checked the queue and it's empty.

while (true) {
    m = q.poll();
    if (m != null) {
        kf.sendMessage(m.topic, m.message);
    } else {
        // Queue was empty, wait for a little while
        // Adjust time based on your requirements
        Thread.sleep(100);
    }
}

You should probably benchmark this to make sure it's actually faster than using a BlockingQueue though.

like image 28
Brendan Long Avatar answered Oct 04 '22 06:10

Brendan Long