Java并发包:阻塞队列(BlockingQueue)

http://blog.csdn.net/zxc123e/article/details/51837866

文章译自:http://tutorials.jenkov.com/java-util-concurrent/index.html
抽空翻译了一下这个教程的文章,后面会陆续放出,如有不妥,请批评指正。
转自请注明出处。

BlockingQueue

在Java.util.concurrent包中的 BlockingQueue接口类是一种线程安全的队列。这篇文章我们将展示如何使用BlockingQueue。

这篇文章不讨论BlockQueue的实现。如果你对此感兴趣,有一片理论的文章 Blocking Queues

BlockingQueue的使用说明

BlockingQueue一般用于这样的场景:一个线程生产对象,另一个线程来消耗对象,下面的插图说明了这个规则:
这里写图片描述
生产线程会持续生产新的对象并把他们插入到队列中,直到队列所能包含对象的最大上限。
如果阻塞队列到达了上限,这时如果尝试插入新的的对象,生产线程将会被阻塞。并且阻塞会一直保持直到消费线程从队列中取出一个对象。

同样,消费线程会持续从阻塞队列中取出对象并处理他们。如果消费线程试图从一个空的队列中取出对象,消费线程将被阻塞住,直到生产线程向队列中加入了一个对象。

BlockingQueue方法。

对于在队列中插入、删除和检查元素操作BlockingQueue有4类不同行为的方法。

operation Throws Exception Special Value Blocks Times Out
Insert add(o) offer(o) put(o) offer(o, timeout, timeunit)
Remove remove(o) poll() take() poll(timeout, timeunit)
Examine element() peek()
四种不同行为的含义如下:

1.抛异常
如果尝试操作是不可能的,一个异常将会抛出。
2.特殊值
如果尝试操作是不可能的,一个特殊值将返回(通常是true/false)
3.阻塞
如果尝试操作是不可能的,方法将会阻塞住,直到可以执行。
4.超时
如果尝试操作是不可能的,方法将会阻塞住,直到可以执行,但是阻塞不会超过给定的时间。并且返回一个特定的值来表示操作是否成功(一般是true/false)。
不能向BlockingQueue中插入null,否则会抛出NullPointerException异常。

访问BlockingQueue中的任意元素也是可能的,不仅仅是在队列前端和末端的元素。例如,你已经排队了一个待处理的对象(有了一个队列),然而你的应用程序需要取消该对象。这时你可以调用remove(o)方法从队列中移除该对象。然而,这种做法不是高效的,因此应该尽量避免使用,除非你真的需要这么做。

BlockingQueue的实现

由于BlockingQueue是一个接口,你需要使用它的具体实现类,java.util.concurrent包中了下面的对于BlockingQueue的具体实现:

ArrayBlockingQueue
DelayQueue
LinkedBlockingQueue
PriorityBlockingQueue
BlockingQueue示例

下面是BlockingQueue的使用示例。这个例子使用了BlockingQueued的一个具体实现类ArrayBlockingQueue。

首先,BlockingQueueExample类开启了Producer和Consumer两个不同的线程,Producer线程向共享BlockingQueue中插入字符串,Consumer线程从中取出它们。

public class BlockingQueueExample {

    public static void main(String[] args) throws Exception {

        BlockingQueue queue = new ArrayBlockingQueue(1024);

        Producer producer = new Producer(queue);
        Consumer consumer = new Consumer(queue);

        new Thread(producer).start();
        new Thread(consumer).start();

        Thread.sleep(4000);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
下面是Producer类,注意每次调用put()方法线程将睡1秒。这将引起Consumer阻塞,一直等待对象加入到队列中。

public class Producer implements Runnable{

    protected BlockingQueue queue = null;

    public Producer(BlockingQueue queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            queue.put("1");
            Thread.sleep(1000);
            queue.put("2");
            Thread.sleep(1000);
            queue.put("3");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
下面是Consumer类。它从队列中取出对象,并打印出来结果。

public class Consumer implements Runnable{

    protected BlockingQueue queue = null;

    public Consumer(BlockingQueue queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            System.out.println(queue.take());
            System.out.println(queue.take());
            System.out.println(queue.take());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ArrayBlockingQueue

ArrayBlockingQueue是一个有边界,阻塞队列,元素存储在内部的数组当中。有边界意味着它不能存储无限的元素。这里有一个可同时存储元素的上边界。你可以在实例化的时候设置这个上边界,但之后这个值不能被改变。

ArrayBlockingQueue内部以FIFO(先进先出)的顺序存储元素。队列头部元素将在队列中保持最长的时间。队列尾部的元素将在队列中保持最短的时间。

这里展示如何实例化和使用一个ArrayBlockingQueue:

BlockingQueue queue = new ArrayBlockingQueue(1024);

queue.put("1");

Object object = queue.take();
1
2
3
4
5
1
2
3
4
5
下面是一个BlockingQueue使用泛型的例子。

BlockingQueue<String> queue = new ArrayBlockingQueue<String>(1024);

queue.put("1");

String string = queue.take();
1
2
3
4
5
1
2
3
4
5
DelayQueue

DelayQueue内部阻塞元素直到某个延迟到期。其中元素必须实现java.concurrent.Delayed接口。下面是Delayed接口:

public interface Delayed extends Comparable<Delayed< {

public long getDelay(TimeUnit timeUnit);

}
1
2
3
4
5
1
2
3
4
5
getDelay()方法返回的值代表元素被释放前应该延迟的时间。如果返回的是0或者负数,延迟被认为是到期的或者说是期满的,接下来在DelayQueue上调用take()等方法后,元素将会释放。

传给getDelay()方法的TimeUnit实例是Enum类型,它判断那种延迟时间单位被返回。TimeUnit的可能的取值:

DAYS
HOURS
MINUTES
SECONDS
MILLISECONDS
MICROSECONDS
Delayed接口也继承了java.lang.Comparable接口,正如你所看到的,这也意味着Delayed的对象可以相互比较。这可能被用在DelayQueue内部给队列中元素排序,这样元素将在期满时将按照顺序被释放。

下面是一个如何使用DelayQueue的例子:

public class DelayQueueExample {

    public static void main(String[] args) {
        DelayQueue queue = new DelayQueue();

        Delayed element1 = new DelayedElement();

        queue.put(element1);

        Delayed element2 = queue.take();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
DelayedElement是我自己创建的对Delayed接口的一个具体实现,它不是java.util.concurrent包中的一部分。你在使用DelayQueue类时必须先创建一个你自己的Delayed接口的具体实现。

LinkedBlockingQueue

LinkedBlockingQueue内部使用一个链表结构保存元素。这种链表结构能够有个理想的上边界,如果没有指定上边界,那么使用Integer.MAX_VALUE作为上边界。

LinkedBlockingQueue内部存储元素遵循FIFO的规则。队列头部元素将在队列中保持最长的时间。队列尾部的元素将在队列中保持最短的时间。

下面是如何实例化和使用LinkedBlockingQueue:

BlockingQueue<String> unbounded = new LinkedBlockingQueue<String>();
BlockingQueue<String> bounded   = new LinkedBlockingQueue<String>(1024);

bounded.put("Value");

String value = bounded.take();
1
2
3
4
5
6
1
2
3
4
5
6
PriorityBlockingQueue

PriorityBlockingQueue是一种无边界的并发队列。它遵循java.util.PriorityQueue类同样的规则。这种队列中不能插入null值。

优先队列是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素。

PriorityQueue是从JDK1.5开始提供的新的数据结构接口。
如果不提供Comparator的话,优先队列中元素默认按自然顺序排列,也就是数字默认是小的在队列头,字符串则按字典序排列。
所有插入到PriorityBlockingQueue 中的元素必须实现java.lang.Comparable接口。因此元素会根据你的Comparable实现来排序。

注意:PriorityBlockingQueue 中不能强制指定元素具有相同的优先级(compare() == 0).
同时也要注意,假使得到PriorityBlockingQueue 的迭代器(Iterator),迭代器不能保证按元素优先级迭代。

下面是一个如何使用PriorityBlockingQueue的例子:

BlockingQueue queue   = new PriorityBlockingQueue();

    //String implements java.lang.Comparable
    queue.put("Value");

    String value = queue.take();
1
2
3
4
5
6
1
2
3
4
5
6
SynchronousQueue

SynchronousQueue 是内部仅包含单一元素的队列。一个向队列中插入元素的线程会被阻塞直到另一个线程从队列中取走元素。同样的,如果一个线程试图取出元素而当前没有元素,这个线程会被阻塞直到另一个线程向队列中插入了元素。
称这个类为一个队列有点夸张。它更像是一个会合点。

猜你喜欢

转载自xshdch.iteye.com/blog/2389753