阻塞队列之LinkedTransferQueue

    TransferQueue是一个继承了BlockingQueue的接口,并且增加若干新的方法。LinkedTransferQueue是TransferQueue接口的实现类,其定义为一个无界的队列,具有先进先出(FIFO)的特性。
     有人这样评价它:"TransferQueue是ConcurrentLinkedQueue、SynchronousQueue (公平模式下)、无界的LinkedBlockingQueues等的超集。"

     LinkedTransferQueue实现了一个重要的接口TransferQueue,该接口含有下面几个重要方法:
     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方法在SynchronousQueue的实现中就已存在了,只是没有做为API暴露出来。SynchronousQueue有一个特性:它本身没有任何内部容量,只能进行线程之间的元素传送。SynchronousQueue在执行offer操作时,如果没有其他线程执行poll,则直接返回false.线程之间元素传送正是通过transfer方法完成的。
     我们知道ThreadPoolExecutor调节线程的原则是:先调整到最小线程,最小线程用完后,他会将优先将任务放入缓存队列(offer(task)),等缓冲队列用完了,才会向最大线程数调节。这似乎与我们所理解的线程池模型有点不同。我们一般采用增加到最大线程后,才会放入缓冲队列中,以达到最大性能。ThreadPoolExecutor代码段:

 1 public void execute(Runnable command) {
 2         if (command == null)
 3             throw new NullPointerException();
 4         /*
 5          * 执行分3步:
 6          *
 7          * 1. 如果运行线程数量少于 corePoolSize , 则尝试用给定的
 8          * command作为它的第一个task启动一个新线程.
 9          * 调用 addWorker 检查运行状态和worker数量, 通过返回false,    
10          * 阻止在出现false alarms 时本不应该添加线程的情况.
11          *
12          * 2. 如果任务排队成功,我们仍然需要再次检测是否我们应该添加一                      
13          * 个线程(因为从最后一次检测之后存在已经死亡的线程)或者池关         
14          * 闭 since entry into this method。所以我们重新检测状态,并且         
15          * 在需要的时候将停止的队列回滚,或者在没有线程的时候启动一个
16          * 新的.
17          *
18          * 3. 如果无法给任务排队则尝试添加新线程.  
19          * 如果失败,则我们知道队列关闭或者饱和了,则拒绝添加新任务
20          */
21         int c = ctl.get();
22         if (workerCountOf(c) < corePoolSize) {
23             if (addWorker(command, true))
24                 return;
25             c = ctl.get();
26         }
27         if (isRunning(c) && workQueue.offer(command)) {
28             int recheck = ctl.get();
29             if (! isRunning(recheck) && remove(command))
30                 reject(command);
31             else if (workerCountOf(recheck) == 0)
32                 addWorker(null, false);
33         }
34         else if (!addWorker(command, false))
35             reject(command);
36     }    

     如果我们采用SynchronousQueue作为ThreadPoolExecutor的缓冲队列时,在没有线程执行poll时(即存在等待线程),则workQueue.offer(command)返回false,这时ThreadPoolExecutor就会增加线程,最快地达到最大线程数。但也仅此而已,也因为SynchronousQueue本身不存在容量,也决定了我们一般无法采用SynchronousQueue作为ThreadPoolExecutor的缓存队列。而一般采用LinkedBlockingQueue的offer方法来实现。最新的LinkedTransferQueue也许可以帮我们解决这个问题。

   transfer算法比较复杂,大致的理解是采用所谓双重数据结构(dual data structures)。之所以叫双重,其原因是方法都是通过两个步骤完成:保留与完成。比如消费者线程从一个队列中取元素,发现队列为空,它就生成一个空元素放入队列,所谓空元素就是数据项字段为空。然后消费者线程在这个字段上旅转等待,这叫保留。直到一个生产者线程意欲向队例中放入一个元素,这里它发现最前面的元素的数据项字段为NULL,它就直接把自已数据填充到这个元素中,即完成了元素的传送。

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

 

生产者和消费者进程模拟

生产者源码(Producer)

 1 import java.util.Random;
 2 import java.util.concurrent.TimeUnit;
 3 import java.util.concurrent.TransferQueue;
 4 
 5 public class Producer implements Runnable {
 6     private final TransferQueue<String> queue;
 7 
 8     public Producer(TransferQueue<String> queue) {
 9         this.queue = queue;
10     }
11 
12     private String produce() {
13         return " you bron at this moment " + (new Date());
14     }
15 
16     @Override
17     public void run() {
18         try {
19             while (true) {
20                 if (queue.hasWaitingConsumer()) {
21                     queue.transfer(produce());
22                 }
23                 TimeUnit.SECONDS.sleep(1);//生产者睡眠一秒钟,这样可以看出程序的执行过程
24             }
25         } catch (InterruptedException e) {
26         }
27     }
28 }

消费者源码(Consumer):

 1 import java.util.concurrent.TransferQueue;
 2 
 3 public class Consumer implements Runnable {
 4     private final TransferQueue<String> queue;
 5 
 6     public Consumer(TransferQueue<String> queue) {
 7         this.queue = queue;
 8     }
 9 
10     @Override
11     public void run() {
12         try {
13             System.out.println(" Consumer " + Thread.currentThread().getName()
14                     + queue.take());
15         } catch (InterruptedException e) {
16         }
17     }
18 }

测试类源码

import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;

public class LuckyNumberGenerator {

    public static void main(String[] args) {
        TransferQueue<String> queue = new LinkedTransferQueue<String>();
        Thread producer = new Thread(new Producer(queue));
        producer.setDaemon(true); //设置为守护进程使得线程执行结束后程序自动结束运行
        producer.start();
        for (int i = 0; i < 10; i++) {
            Thread consumer = new Thread(new Consumer(queue));
            consumer.setDaemon(true);
            consumer.start();
            try {
                // 消费者进程休眠一秒钟,以便生产者获得CPU,从而生产产品
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果(一种可能的结果):


1  Consumer Thread-1 your has bron at this moment Wed Sep 20 10:50:44 CST 2017
2  Consumer Thread-2 your has bron at this moment Wed Sep 20 10:50:45 CST 2017
3  Consumer Thread-3 your has bron at this moment Wed Sep 20 10:50:46 CST 2017
4  Consumer Thread-4 your has bron at this moment Wed Sep 20 10:50:47 CST 2017
5  Consumer Thread-5 your has bron at this moment Wed Sep 20 10:50:48 CST 2017
6  Consumer Thread-6 your has bron at this moment Wed Sep 20 10:50:49 CST 2017
7  Consumer Thread-7 your has bron at this moment Wed Sep 20 10:50:50 CST 2017
8  Consumer Thread-8 your has bron at this moment Wed Sep 20 10:50:51 CST 2017
9  Consumer Thread-9 your has bron at this moment Wed Sep 20 10:50:52 CST 2017

原文地址:http://blog.csdn.net/yjian2008/article/details/16951811

posted @ 2017-09-20 10:30  尽情折叠我吧  阅读(525)  评论(0)    收藏  举报