自己测试通过的cas版本的多读多写的无锁模式的循序队列
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceArray;
/*cas的缓存循环数组,
* 存数据:采用先占用位置,然后在放数据的原则.
* 取数据:采用先取数据,置为cas置为空,然后在将读指针加一的原则,
* 需要注意的时候,由于是循环数组,取出来数据不为空,有可能已经是又被放进去的数据了.要保证当前reader指针没被改变的情况下才可以去cas改变置为空.
* @auth zhangwy
* */
public class MpsQueue<T> {
protected AtomicLong readerIndex;
protected AtomicLong writerIndex;
protected final int size ;
protected final int mask ;
AtomicReferenceArray<T> buffer = null;
public MpsQueue(int length) {
size = MathUtil.roundToPowerOfTwo(length) ;
mask = size - 1;
buffer = new AtomicReferenceArray<T>(size);
readerIndex = new AtomicLong(0);
writerIndex = new AtomicLong(0);
}
//idx % size
public int idx(long index) {
return (int) (index & mask);
}
//读指针获取
public long getReaderIndex() {
return readerIndex.get();
}
//写指针获取
public long getWriterIndex() {
return writerIndex.get();
}
/*放置元素*/
public boolean put(T element) {
long reader ,writer ;
long wrapSize ;
if(element == null) {
throw new IllegalArgumentException("element can't not be null");
}
do {
reader = getReaderIndex();
writer = getWriterIndex();
wrapSize = writer - reader ;
//仍有空间放置
if(wrapSize < size) {
} else {
return false;
}
} while(!writerIndex.compareAndSet(writer, writer + 1));
int offset = idx(writer);
if(buffer.get(offset) != null) {
throw new RuntimeException("buffer @" + writer + " must be null,but now is not null");
}
buffer.set(offset, element);
return true;
}
/**
** get a element from the queue
***/
public T get() {
T element = null;
long reader ,writer ;
int offset;
do {
reader = getReaderIndex() ;
writer = getWriterIndex();
/*queue不为空*/
if(writer == reader) {
return null;
}
offset = idx(reader);
element = buffer.get(offset);
/*元素已经被取走了*/
if(element == null ) continue;
/*reader已经是最新*/
if(reader == getReaderIndex() && buffer.compareAndSet(offset, element, null)) {
//System.out.println("pop :" +reader +":"+ offset + ":" + element);
break;
}
}while(true);
if(element != null) {
while(!readerIndex.compareAndSet(reader, reader + 1)){
throw new RuntimeException(String.format("readerIndex is %d has been changed,is error", readerIndex));
}
return element;
}
return null;
}
/**
*
*/
}
public class MathUtil {
public static int log2p(int x) {
int r = 0;
while ((x >>= 1) != 0)
r++;
return r;
}
public static int roundToPowerOfTwo(int x) {
x --;
x |= x >>> 1;
x |= x >>> 2;
x |= x >>> 4;
x |= x >>> 8;
x |= x >>> 16;
x++;
if (x < 0) {
x >>>= 1;
}
return x;
}
public static void main(String[] args) {
System.out.println(roundToPowerOfTwo(699999999));
}
}
测试代码
package io.mycat.bigmem.buffer;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Test;
import io.mycat.bigmem.util.MpsQueue;
/**
*@desc
*@author zhangwy @date 2017年1月2日 下午6:02:44
**/
public class DirectByteBufferTest {
@Test
public void testMPIMulThread() {
int length = 16;
MpsQueue<Integer> array = new MpsQueue<Integer>(length );
ReadThread readThread = new ReadThread(array);
readThread.start();
ReadThread readThread1 = new ReadThread(array);
readThread1.start();
new WriteThread(0, 1000000000, array).start();
new WriteThread(1000000000, 2000000000, array).start();
new WriteThread(2000000000, 2100000000, array).start();
// new WriteThread(3000000000, 4000000000, array).start();
//new WriteThread(400000, 500000, array).start();
while(readThread.isAlive()){
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class WriteThread<Integer> extends Thread {
protected int start;
protected int end;
protected MpsQueue<Integer> queue;
public WriteThread(int start ,int end,MpsQueue<Integer> queue) {
this.start = start;
this.end = end;
this.queue = queue;
}
@Override
public void run() {
int content = start;
for(; content < end;) {
final Integer p = (Integer) new java.lang.Integer(content);
if(queue.put(p)) {
content ++;
} else {
try {
Thread.currentThread().sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("======" + content);
}
}
class ReadThread<Integer> extends Thread {
protected AtomicLong count ;
MpsQueue<Integer> queue = null;
public ReadThread(MpsQueue<Integer> queue) {
this.queue = queue;
count = new AtomicLong();
}
@Override
public void run() {
Integer element = null;
while(true) {
while((element = queue.get()) != null) {
count.incrementAndGet();
}
System.out.println(count.get());
;
try {
Thread.currentThread().sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}