/**
* author:gaoxingliang@outlook.com
* created:2015年9月2日 下午8:19:08
*/
package datastruct;
import java.util.concurrent.Semaphore;
/**
* 使用信号量实现的有界缓冲池
*/
public class BoundedBuffer<T>
{
private int size;
// 可用的信号量
private Semaphore avialable = null;
// 是否已经有元素的信号量
private Semaphore used = null;
// put的位置 take的位置
private int putPos, takePos;
private T[] objs;
@SuppressWarnings("unchecked")
public BoundedBuffer(int size)
{
this.size = size;
avialable = new Semaphore(size);
used = new Semaphore(0);
objs = (T[]) new Object[size];
putPos = takePos = 0;
}
public boolean isEmpty()
{
return used.availablePermits() == 0;
}
public boolean isFull()
{
return avialable.availablePermits() == 0;
}
/**
* 取一个元素
*
* @return
* @throws InterruptedException
*/
public T take() throws InterruptedException
{
// wrong version
T o = null;
used.acquire();
synchronized (this)
{
o = objs[takePos];
objs[takePos] = null;
takePos = (takePos + 1) % size;
}
avialable.release();
return o;
}
/**
* 放一个元素
*
* @param o
* @throws InterruptedException
*/
public void put(T o) throws InterruptedException
{
avialable.acquire();
synchronized (this)
{
objs[putPos] = o;
putPos = (putPos + 1) % size;
}
used.release();
}
}
测试程序:
/**
*
* author:gaoxingliang@outlook.com
* created:2015年9月2日 下午8:43:37
*/
package datastruct;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.security.SecureRandom;
import org.junit.Ignore;
import org.junit.Test;
/**
* @author gxl
*
*/
public class BoundedBufferTestCase
{
@Test
public void testbase1()
{
// 测试基础方法
BoundedBuffer<Integer> bb = new BoundedBuffer<Integer>(10);
assertTrue(bb.isEmpty());
assertFalse(bb.isFull());
}
@Test
public void testIsFullWhenPuts() throws InterruptedException
{
int size = 10;
BoundedBuffer<Integer> bb = new BoundedBuffer<Integer>(size);
assertTrue(bb.isEmpty());
assertFalse(bb.isFull());
for (int i = 0; i < size; i++)
{
bb.put(i);
}
assertTrue(bb.isFull());
assertFalse(bb.isEmpty());
}
@Ignore
public void testTakeWhenEmpty()
{
int size = 10;
final BoundedBuffer<Integer> bb = new BoundedBuffer<Integer>(size);
Thread taker = new Thread(new Runnable()
{
@Override
public void run()
{
try
{
bb.take();
fail("Cannt take suc.");
}
catch (InterruptedException e)
{
}
}
});
try
{
taker.start();
Thread.sleep(10000);
taker.interrupt();
taker.join(10000);
assertFalse(taker.isAlive());
}
catch (InterruptedException e)
{
fail(e.getMessage());
}
}
@Test
public void testCorrectness()
{
SecureRandom r = new SecureRandom();
for (int i = 0; i< 10; i++)
{
boolean res = new PutTakeTest(r.nextInt(20000) + 1, r.nextInt(35) + 10, r.nextInt(50) + 10).test();
assertTrue(res);
}
}
}
/**
*
* author:gaoxingliang@outlook.com
* created:2015年9月2日 下午8:59:46
*/
package datastruct;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 辅助测试BoundedBuffer安全性的测试
*
* @author gxl
*
*/
public class PutTakeTest
{
private int tryTime, pairCount;
private final AtomicInteger putSum = new AtomicInteger();
private final AtomicInteger takeSum = new AtomicInteger();
private CyclicBarrier barrier;
private static final ExecutorService es = Executors.newCachedThreadPool();
private BoundedBuffer<Integer> bb;
/**
* constructor
*
* @param tryTime
* 测试次数/累加次数
* @param pairCount
* 生产者和消费者各pairCount个
* @param buffSize
* 缓冲池大小
*/
public PutTakeTest(int tryTime, int pairCount, int buffSize)
{
this.tryTime = tryTime;
this.pairCount = pairCount;
this.barrier = new CyclicBarrier(pairCount * 2 + 1);
this.bb = new BoundedBuffer<Integer>(buffSize);
}
public boolean test()
{
try
{
for (int i = 0; i< pairCount; i ++)
{
es.execute( new Producer(bb, barrier));
es.execute(new Consumer(bb, barrier));
}
barrier.await();//WAIT UNTIL ALL IS READY
barrier.await();//WAIT UNTIL ALL IS FINISHED
return putSum.get() == takeSum.get();
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
}
class Producer implements Runnable
{
private BoundedBuffer<Integer> outBuffer;
private CyclicBarrier outBarrier;
/**
* @param bb
* @param barrier
*/
public Producer(BoundedBuffer<Integer> bb, CyclicBarrier barrier)
{
super();
this.outBuffer = bb;
this.outBarrier = barrier;
}
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
@Override
public void run()
{
try
{
int seed = (this.hashCode() ^ (int) System.nanoTime());
outBarrier.await();
int sum = 0;
for (int i = 0; i < tryTime; i++)
{
outBuffer.put(seed);
sum += seed;
seed = xOrShift(seed);
}
putSum.getAndAdd(sum);
outBarrier.await();
}
catch (Exception e)
{
// TODO: handle exception
}
}
}
class Consumer implements Runnable
{
private BoundedBuffer<Integer> outBuffer;
private CyclicBarrier outBarrier;
/**
* @param bb
* @param barrier
*/
public Consumer(BoundedBuffer<Integer> bb, CyclicBarrier barrier)
{
super();
this.outBuffer = bb;
this.outBarrier = barrier;
}
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
@Override
public void run()
{
try
{
outBarrier.await();
int sum = 0;
for (int i = 0; i < tryTime; i++)
{
sum += outBuffer.take();
}
takeSum.getAndAdd(sum);
outBarrier.await();
}
catch (Exception e)
{
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
static int xOrShift(int y)
{
y ^= (y << 6);
y ^= (y >>> 21);
y ^= (y << 7);
return y;
}
}