package com.bao.examples.thread;
import java.util.Iterator;
import java.util.LinkedList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class QueueTokenPool implements TokenPool
{
private static Log log = LogFactory.getLog(QueueTokenPool.class);
private static final int QUE_CONTENT_NONE = 0;
private static final int QUE_CONTENT_HEAD = 1;
private static final int QUE_CONTENT_FULL = 2;
private int count = -1;
private long[] tokenHolders = null;
private LinkedList<Thread> requestQueue = null;
private int capacity = -1;
public QueueTokenPool()
{
this(-1);
}
public QueueTokenPool(int queueSize)
{
count = MAX_TOKEN_COUNT;
if(count <= 0)
{
throw new IllegalArgumentException("Invalid total token count = "
+ count);
}
tokenHolders = new long[count];
for(int i = 0; i < count; i++)
{
tokenHolders[i] = -1;
}
requestQueue = new LinkedList<Thread>();
this.capacity = (queueSize <= 0) ? -1 : queueSize;
log.info("TokenPool TotalCount = " + count + ", capacity = " + capacity);
}
public int getToken()
{
synchronized(this)
{
if(log.isDebugEnabled())
{
log.debug("[Pre Enqueue]- " + getCurrentState(QUE_CONTENT_FULL));
}
//If there's NO wait seats and NO available token count, throw exception
if(capacity > 0 && (requestQueue.size() - count) >= capacity)
{
log.info("The waiting queue is already full. " + getCurrentState(QUE_CONTENT_NONE));
return -1;
}
Thread t = Thread.currentThread();
requestQueue.add(t);
if(log.isDebugEnabled())
{
log.debug("[Post Enqueue]- " + getCurrentState(QUE_CONTENT_FULL));
}
//1. No available tokens
//2. The current thread is not at the first of the waiting queue.
while(count <= 0 || t != requestQueue.peek())
{
try
{
log.info("getToken [WAIT]- " + getCurrentState(QUE_CONTENT_HEAD));
// Wait until the condition changed
wait();
}
catch(InterruptedException e)
{
log.info("The thread is interrupted.", e);
Thread.currentThread().interrupt(); // very important
}
}
//1. The current thread is the first one in the queue
//2. There are available tokens.
for(int i = tokenHolders.length - 1; i >= 0; i--)
{
if(tokenHolders[i] == -1)
{
log.info("getToken [SUCC]- " + getCurrentState(QUE_CONTENT_HEAD));
if(log.isDebugEnabled())
{
log.debug("[Pre Dequeue]- " + getCurrentState(QUE_CONTENT_FULL));
}
tokenHolders[i] = Thread.currentThread().getId();
count--;
// Remove the current thread from queue
requestQueue.removeFirst();
if(log.isDebugEnabled())
{
log.debug("[Post Dequeue]- " + getCurrentState(QUE_CONTENT_FULL));
}
//if there are still available tokens and the queue is not empty
//notify the threads in the queue
if(count > 0 && !requestQueue.isEmpty())
{
notifyAll();
}
return i;
}
}
log.fatal("This should not happen. " + getCurrentState(QUE_CONTENT_FULL));
return -1;
}
}
public void releaseToken(int token)
{
synchronized(this)
{
if(token < 0 || token >= tokenHolders.length)
{
log.error("This should be an error call this API. token = " + token);
return;
}
if(tokenHolders[token] == Thread.currentThread().getId())
{
tokenHolders[token] = -1;
count++;
// count must be greater than 0, skip this condition check.
// if(count > 0 && !requestQueue.isEmpty())
if(!requestQueue.isEmpty())
{
notifyAll();
}
}
}
}
public int getTotalCount()
{
synchronized(this)
{
return tokenHolders.length;
}
}
public int getReservedCount()
{
synchronized(this)
{
if(count <= 0)
{
return tokenHolders.length;
}
return tokenHolders.length - count;
}
}
private String getCurrentState(int queContentType)
{
StringBuilder sb = new StringBuilder("[State]: ");
sb.append("TokenCount=").append(count).append(", ");
sb.append("QueueSize=").append(requestQueue.size());
switch(queContentType)
{
case QUE_CONTENT_HEAD:
{
sb.append(", ");
if(requestQueue.isEmpty())
{
sb.append("QueueHead=[]");
}
else
{
sb.append("QueueHead=[");
sb.append(requestQueue.peek().getName());
sb.append("]");
}
break;
}
case QUE_CONTENT_FULL:
{
sb.append(", QueueContent=");
sb.append(getQueueContent(requestQueue));
break;
}
case QUE_CONTENT_NONE:
default:
{
break;
}
}
return sb.toString();
}
private String getQueueContent(LinkedList<Thread> queue)
{
StringBuilder sb = new StringBuilder("[");
for(Iterator<Thread> iter = queue.iterator(); iter.hasNext(); )
{
Thread t = iter.next();
sb.append(getShortThreadName(t));
if(iter.hasNext())
{
sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
private String getShortThreadName(Thread t)
{
String threadName = t.getName();
if(threadName.contains("@qtp-"))
{
int index = threadName.lastIndexOf('-');
if(threadName.length() > index + 1)
{
threadName = threadName.substring(index + 1);
}
}
return threadName;
}
}