Java并发50:并发集合系列-基于独占锁实现的双向阻塞队列LinkedBlockingDeque

原文地址:http://ifeve.com/concurrent-collections-3/

关于与LinkedBlockingDeque类似的单向队列LinkedBlockingQueue可以参考:Java并发49

使用阻塞线程安全的列表

列表(list)是最基本的集合。

一个列表中的元素数量是不确定的,并且你可以添加、读取和删除任意位置上的元素。

并发列表允许不同的线程在同一时刻对列表里的元素进行添加或删除,而不会产生任何数据不一致的问题。

在这个指南中,你将学习如何在你的并发应用程序中使用阻塞的列表。

阻塞列表与非阻塞列表的主要区别是,阻塞列表有添加和删除元素的方法,如果由于列表已满或为空而导致这些操作不能立即进行,它们将阻塞调用的线程,直到这些操作可以进行。

Java包含实现阻塞列表的LinkedBlockingDeque类。

你将使用以下两种任务来实现例子:

  • 添加大量数据到列表。
  • 从同一个列表中删除大量的数据。

准备工作…

这个指南的例子使用Eclipse IDE实现。如果你使用Eclipse或其他IDE,如NetBeans,打开它并创建一个新的Java项目。

如何做…

按以下步骤来实现的这个例子:

1.创建一个实现Runnable接口的Client类。

public class Client implements Runnable{

2.声明一个私有的、LinkedBlockingDeque类型的、参数化为String类的属性requestList。

private LinkedBlockingDeque requestList;

3.实现这个类的构造器,并初始化它的属性。

public Client (LinkedBlockingDeque requestList) {
    this.requestList=requestList;
}

4.实现run()方法。使用requestList对象的put()方法,每秒往列表插入5个String对象。重复这个循环3次。

@Override
public void run() {
    for (int i=0; i<3; i++) {
        for (int j=0; j<5; j++) {
            StringBuilder request=new StringBuilder();
            request.append(i);
            request.append(":");
            request.append(j);
            try {
                requestList.put(request.toString());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.printf("Client: %s at %s.\n",request,new Date());
        }
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    System.out.printf("Client: End.\n");
}

5.创建这个例子的主类,通过实现Main类,并实现main()方法。

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

6.声明和创建参数化为String类、名为list的LinkedBlockingDeque。

LinkedBlockingDeque
list=new LinkedBlockingDeque(3);

7.创建和启动一个Thread对象来执行client任务。

Client client=new Client(list);
Thread thread=new Thread(client);
thread.start();

8.使用这个列表的take()方法,每300毫秒获取列表的3个字符串(String)对象。重复这个循环5次。将字符串(String)写入到控制台。

for (int i=0; ifor (int j=0; j<3; j++) {
    String request=list.take();
    System.out.printf("Main: Request: %s at %s. Size:%d\n",request,new Date(),list.size());
}
TimeUnit.MILLISECONDS.sleep(300);

9.写入一条信息表明程序的结束。

System.out.printf("Main: End of the program.\n");

它是如何工作的…

在这个指南中,你已使用参数化为String类的LinkedBlockingDeque来处理非阻塞并发列表的数据。

Client类使用put()方法添加字符串到列表中。如果列表已满(因为你已使用固定大小来创建它),这个方法阻塞线程的执行,直到列表有可用空间。

Main类使用take()方法从列表中获取字符串,如果列表为空,这个方法将阻塞线程的执行,直到列表中有元素。

在这个例子中,使用LinkedBlockingDeque类的这两个方法,如果它们在阻塞时被中断,将抛出InterruptedException异常。所以,你必须包含必要的代码来捕捉这个异常。

不止这些…

LinkedBlockingDeque类同时提供方法用于添加和获取列表的元素,而不被阻塞,或抛出异常,或返回null值。这些方法是:

  • takeFirst() 和takeLast():这些方法分别返回列表的第一个和最后一个元素。它们从列表删除返回的元素。如果列表为空,这些方法将阻塞线程,直到列表有元素。
  • getFirst() 和getLast():这些方法分别返回列表的第一个和最后一个元素。它们不会从列表删除返回的元素。如果列表为空,这些方法将抛出NoSuchElementExcpetion异常。
  • peek()、peekFirst(),和peekLast():这些方法分别返回列表的第一个和最后一个元素。它们不会从列表删除返回的元素。如果列表为空,这些方法将返回null值。
  • poll()、pollFirst()和 pollLast():这些方法分别返回列表的第一个和最后一个元素。它们从列表删除返回的元素。如果列表为空,这些方法将返回null值。
  • add()、 addFirst()、addLast():这些方法分别在第一个位置和最后一个位置上添加元素。如果列表已满(你已使用固定大小创建它),这些方法将抛出IllegalStateException异常。
 
posted @ 2021-10-23 14:20  姚春辉  阅读(73)  评论(0编辑  收藏  举报