package com.gvaneyck.util; import java.util.concurrent.Semaphore; /** * A thread safe queue with automatic resizing * Supports multiple readers and multiple writers * * @author Gvaneyck */ public class TSQueue<T> { public static final double EXPANSION_FACTOR = 1.5; private int front; private int back; private int size; private Object[] data; // Semaphore rather than Lock since it needs to be released from a separate // thread Semaphore reading = new Semaphore(1); Semaphore writing = new Semaphore(1); /** * Creates a queue with size 10 */ public TSQueue() { this(10); } /** * Creates a queue with the specified starting size * * @param size */ public TSQueue(int size) { this.size = size; data = new Object[size]; } /** * Gets the object at the front of the queue or null if the queue is empty * * @return */ public T get() { reading.acquireUninterruptibly(); if (front == back) { reading.release(); return null; } Object ret = data[front % size]; front++; reading.release(); return (T)ret; } /** * Adds an object to the end of the queue * * @param val */ public void put(T val) { writing.acquireUninterruptibly(); if (back == front + size) reallocate(); data[back % size] = val; back++; writing.release(); } /** * Expands the current array and copies over the old data * Note that the write lock is already acquired at this stage */ private void reallocate() { reading.acquireUninterruptibly(); final int oldFront = front; final int oldBack = back; final int oldSize = size; final Object[] oldData = data; front = 0; back = size; size = (int)(EXPANSION_FACTOR * size); data = new Object[size]; // Block gets (and reallocating), but don't block puts while copying the // data back new Thread() { public void run() { int j = 0; for (int i = oldFront; i < oldBack; i++, j++) data[j] = oldData[i % oldSize]; reading.release(); } }.start(); } }