org.jgroups.util.ConcurrentLinkedBlockingQueue2 Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.util;
import java.util.Collection;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Attempt at writing a fast transfer queue, which is bounded. The take() method blocks until there is an element, but
* the offer() method drops the element and returns if the queue is full (doesn't block).
* The design assumes a number of producers but only one consumer. The consumer only blocks when the queue is
* empty (on the not-empty condition), the producers block when the queue is full (on the not-full condition). The
* producers increment a count atomically and if the count is greater than the capacity, they block on the not-full
* condition. The consumer decrements the condition and signals the not-full condition when the count is capacity -1
* (from capacity to capacity-1).
* The producers signal not-empty when the count is 1 (from 0 to 1)
*
* @author Bela Ban
* @since 3.5
*/
public class ConcurrentLinkedBlockingQueue2 extends ConcurrentLinkedQueue implements BlockingQueue {
private static final long serialVersionUID=2539983016900218313L;
protected final int capacity;
protected final AtomicInteger count=new AtomicInteger(0);
// not_empty is signalled by a producer when count went from 0 to 1
protected final Lock not_empty_lock=new ReentrantLock();
protected final Condition not_empty=not_empty_lock.newCondition();
public ConcurrentLinkedBlockingQueue2(int capacity) {
this.capacity=capacity;
Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("** num_awaits=" + not_empty_awaits)));
/* Thread thread=new Thread() {
public void run() {
while(true) {
System.out.println("*** size=" + size());
Util.sleep(1000);
}
}
};
thread.setDaemon(true);
thread.start();*/
}
int not_empty_awaits=0;
/**
* Drops elements if capacity has been reached. That's OK for the ThreadPoolExecutor as dropped messages
* will get retransmitted
* @param t
* @return
*/
public boolean offer(T t) {
boolean retval=super.offer(t);
if(retval) count.incrementAndGet();
return retval;
}
public T take() throws InterruptedException {
T val=super.poll();
if(val != null) {
decrCount();
return val;
}
waitForNotEmpty();
// at this stage, we are guaranteed to have a value
val=super.poll();
if(val != null)
decrCount();
return val;
}
public T poll() {
T val=super.poll();
if(val != null)
decrCount();
return val;
}
public T poll(long timeout, TimeUnit unit) throws InterruptedException {
return null;
}
public boolean remove(Object o) {
boolean retval=super.remove(o);
if(retval)
decrCount();
return retval;
}
public int remainingCapacity() {
return capacity - size();
}
public int drainTo(Collection super T> c) {
int cnt=0;
if(c == null)
return cnt;
for(;;) {
T el=poll();
if(el == null)
break;
c.add(el);
cnt++;
}
count.set(0);
return cnt;
}
public void put(T t) throws InterruptedException {
if(super.offer(t))
incrCount();
}
public boolean offer(T t, long timeout, TimeUnit unit) throws InterruptedException {
return offer(t);
}
public int size() {
return count.get();
}
public int drainTo(Collection super T> c, int maxElements) {
return drainTo(c);
}
protected void waitForNotEmpty() throws InterruptedException {
while(count.get() == 0) {
not_empty_lock.lock();
try {
// System.out.println("-----> waiting for not empty: num_awaits=" + ++not_empty_awaits + ", count=" + count);
if(count.get() > 0)
return;
not_empty_awaits++;
not_empty.await();
}
finally {
not_empty_lock.unlock();
}
}
}
protected void decrCount() {
count.getAndDecrement();
}
protected void incrCount() {
int prev_count=count.getAndIncrement();
if(prev_count == 0) {
not_empty_lock.lock();
try {
not_empty.signal(); // not signalAll() as there is only *one* consumer !
}
finally {
not_empty_lock.unlock();
}
}
}
}