Fork/Join模式(JSR166y)手记之TransferQueue/LinkedTransferQueue

TransferQueue是一个继承了 BlockingQueue的接口,并且增加若干新的方法。LinkedTransferQueue是实现类,其定义为一个无界的队列,一样具有先进先出(FIFO : first-in-first-out)的特性。
Doug Lea 这样评价它:
TransferQueue是一个聪明的队列,它是ConcurrentLinkedQueue, SynchronousQueue (在公平模式下), 无界的LinkedBlockingQueues等的超集。
显然易见,混合了若干高级特性,并且具有高性能的一个组合体,一个多面手。
这里有一个有关LinkedTransferQueue的Doug Lea等人所撰写论文,讨论了其算法、性能等,地址:http://www.cs.rice.edu/~wns1/papers/2006-PPoPP-SQ.pdf
单纯从队列来看,TransferQueue接口增加了一些很实用的新特性,其transfer方法提供了线程之间直接交换对象的捷径,下面一一说来。
  1. transfer(E e)
    若当前存在一个正在等待获取的消费者线程,即立刻移交之;否则,会插入当前元素e到队列尾部,并且等待进入阻塞状态,到有消费者线程取走该元素。
  2. tryTransfer(E e)
    若当前存在一个正在等待获取的消费者线程(使用take()或者poll()函数),使用该方法会即刻转移/传输对象元素e;
    若不存在,则返回false,并且不进入队列。
    这是一个不阻塞的操作。
  3. tryTransfer(E e, long timeout, TimeUnit unit)
    若当前存在一个正在等待获取的消费者线程,会立即传输给它;
    否则将插入元素e到队列尾部,并且等待被消费者线程获取消费掉,
    若在指定的时间内元素e无法被消费者线程获取,则返回false,同时该元素被移除。
  4. hasWaitingConsumer()
    很明显,判断是否终端消费者线程
  5. getWaitingConsumerCount()
    字面意思很明白,获取终端所有等待获取元素的消费线程数量
  6. size()
    因为队列的异步特性,检测当前队列的元素个数需要逐一迭代,可能会得到一个不太准确的结果,尤其是在遍历时有可能队列发生更改。
  7. 批量操作
    类似于 addAll,removeAll, retainAll, containsAll, equals, toArray 等方法,API不能保证一定会立刻执行。
    因此,我们在使用过程中,不能有所期待,这是一个具有异步特性的队列。

注意事项:
  • 无论是transfer还是tryTransfer方法,在>=1个消费者线程等待获取元素时(此时队列为空),都会立刻转交,这属于线程之间的元素交换。注意,这时,元素并没有进入队列。
  • 在队列中已有数据情况下,transfer将需要等待前面数据被消费掉,直到传递的元素e被消费线程取走为止。
  • 使用transfer方法,工作者线程可能会被阻塞到生产的元素被消费掉为止
  • 消费者线程等待为零的情况下,各自的处理元素入队与否情况有所不同。
  • size()方法,需要迭代,可能不太准确,尽量不要调用。

或许,下次我们在构造一个线程池时,可以考虑使用TransferQueue:
public static ExecutorService newTransferCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new LinkedTransferQueue());
    }


简单测试代码:

 
 
package com . learn . jsry166y . demo . queue ;
import java.util.concurrent.TimeUnit ;
import jsr166y.LinkedTransferQueue ;
import jsr166y.Phaser ;
import jsr166y.TransferQueue ;
import junit.framework.Assert ;
import org.junit.Test ;
/**
* 测试LinkedTransferQueue的一些特性
*
* @author yongboy
* @time 2012-2-1
* @version 1.0
*/
public class TestTransferQueue {
final static TransferQueue < String > queue = new LinkedTransferQueue < String >();
// 模拟transfer等待消费者线程,若消费者线程不出现的话,则阻塞
@Test
public void testTransfer () {
try {
// 优先启动一个线程,做消费者线程,去取队列数据
new Thread () {
@Override
public void run () {
try {
Thread . sleep ( 5000L );
} catch ( InterruptedException e ) {
e . printStackTrace ();
}
try {
// 取完元素,transfer不再阻塞
String result = queue . take ();
System . out . println ( "result : " + result );
} catch ( InterruptedException e ) {
e . printStackTrace ();
}
}
}. start ();
queue . transfer ( "hello0" );
System . out . println ( "the data had been taked" );
} catch ( InterruptedException e1 ) {
e1 . printStackTrace ();
}
Assert . assertEquals ( queue . size (), 0 );
}
// 模拟若消费者线程等待获取元素,元素不存在情况下,进入阻塞状态
@Test
public void testTake () {
try {
// 优先启动一个线程,做消费者线程,去取队列数据
new Thread () {
@Override
public void run () {
try {
long start = System . currentTimeMillis ();
// 取完元素,transfer不再阻塞
String result = queue . take ();
System . out . println ( "result : " + result
+ " use time : "
+ ( System . currentTimeMillis () - start ));
} catch ( InterruptedException e ) {
e . printStackTrace ();
}
}
}. start ();
Thread . sleep ( 2000L );
queue . transfer ( "hello-0" );
} catch ( InterruptedException e1 ) {
e1 . printStackTrace ();
}
Assert . assertEquals ( queue . size (), 0 );
}
// 模拟获取等待消费者线程的个数
@Test
public void testWaitingConsumerCount () {
int consumerCount = - 1 ;
int threadNum = 5 ;
try {
for ( int i = 0 ; i < threadNum ; i ++) {
new Thread () {
@Override
public void run () {
try {
long start = System . currentTimeMillis ();
String result = queue . take ();
System . out . println ( "result : " + result
+ " use time : "
+ ( System . currentTimeMillis () - start ));
} catch ( InterruptedException e ) {
e . printStackTrace ();
}
}
}. start ();
}
Thread . sleep ( 2000L );
consumerCount = queue . getWaitingConsumerCount ();
for ( int i = 0 ; i < consumerCount ; i ++) {
queue . transfer ( "hello-0-" + i );
}
} catch ( InterruptedException e1 ) {
e1 . printStackTrace ();
}
Assert . assertEquals ( consumerCount , threadNum );
}
@Test
public void testTryTransfer () {
queue . tryTransfer ( "hello" );
try {
Thread . sleep ( 10 );
} catch ( InterruptedException e ) {
e . printStackTrace ();
}
Assert . assertEquals ( queue . size (), 0 );
}
@Test
public void testTryTimedTransfer () {
try {
queue . tryTransfer ( "hello2" , 100L , TimeUnit . MILLISECONDS );
} catch ( InterruptedException e1 ) {
e1 . printStackTrace ();
}
try {
Thread . sleep ( 100L );
} catch ( InterruptedException e ) {
e . printStackTrace ();
}
Assert . assertEquals ( queue . size (), 0 );
}
@Test
public void testAdd () {
queue . add ( "hello3" );
Assert . assertEquals ( queue . size (), 1 );
}
@Test
public void testGet () {
String result = queue . poll ();
Assert . assertEquals ( result , "hello3" );
}
// 如何模拟,在队列有数据的情况下,测试transfer
@Test
public void testTransferWithOldElement () {
final int COUNT = 1001 ;
final Phaser barrier = new Phaser ( COUNT );
final StringBuilder sb = new StringBuilder ();
try {
for ( int i = 0 ; i < 999 ; i ++) {
queue . add ( "hello_0_" + i );
}
for ( int i = 0 ; i < 1000 ; i ++) {
// 优先启动一个线程,做消费者线程,去取队列数据
new Thread () {
@Override
public void run () {
try {
barrier . arriveAndAwaitAdvance ();
// 取完元素,transfer不再阻塞
String result = queue . take ();
if ( result . startsWith ( "_hello_" )) {
System . out . println ( "thread name : "
+ Thread . currentThread (). getName ()
+ " ; queue size : " + queue . size ());
sb . append ( result );
// 模拟主线程等待时间
Thread . sleep ( 1000L );
// 终止,主线程得以继续
barrier . forceTermination ();
}
} catch ( InterruptedException e ) {
e . printStackTrace ();
}
}
}. start ();
}
barrier . arriveAndAwaitAdvance ();
queue . transfer ( "_hello_" );
} catch ( Exception e1 ) {
e1 . printStackTrace ();
}
long start = System . currentTimeMillis ();
barrier . arriveAndAwaitAdvance ();
System . out . println ( "wait use time : "
+ ( System . currentTimeMillis () - start ));
Assert . assertEquals ( sb . toString (), "_hello_" );
}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值