使用阻塞线程安全双端队列

Java 9并发编程指南 目录

List作为最基本的集合,包含没有限制数量的元素,可以在任何位置增加、读取,或者删除元素。并发列表允许不同线程在同一时间添加或删除来自列表中的元素,而不会产生任何数据非一致性错误。与列表相似的,还有双端队列,其数据结构与队列相近,但在双端队列中,可以在前(头)后(尾)端添加或删除。

本节将学习在并发应用中如何使用阻塞双端队列。阻塞和非阻塞双端队列的主要区别是,阻塞双端队列的插入和删除元素方法,如果因为列表已满或者为空而没有立即执行,则阻塞发起调用的线程,直到可以执行操作。Java提供了LinkedBlockingDeque类来实现阻塞双端队列。

将要实现的范例中包含如下两个不同任务:

  • 向双端队列添加大量元素
  • 从相同的列表中大规模删除数据

准备工作

本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。

实现过程

通过如下步骤实现范例:

  1. 创建名为Client的类,指定其实现Runnable接口:

    public class Client implements Runnable {
    
  2. 声明名为requestList的String类参数化的私有LinkedBlockingDeque属性:

    	private final LinkedBlockingDeque<String> requestList;
    
  3. 实现类构造函数,初始化属性:

    	public Client (LinkedBlockingDeque<String> requestList) {
    		this.requestList=requestList;
    	}
    
  4. 实现run()方法,使用requestList对象的put()方法每隔一秒向双端队列插入五个String对象,循环重复三次:

    	@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 added: %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. 声明和创建名为list的String类参数化的LinkedBlockingDeque属性,固定队列数量为3:

    		LinkedBlockingDeque<String> list=new LinkedBlockingDeque<>(3);
    
  7. 创建和启动一个Thread对象,执行Client任务:

    		Client client=new Client(list);
    		Thread thread=new Thread(client);
    		thread.start();
    
  8. 使用列表对象的take()方法每隔300毫秒从列表中得到三个String对象,循环重复五次。输出字符串到控制台:

    		for (int i=0; i<5 ; i++) {
    			for (int j=0; j<3; j++) {
    				String request=list.take();
    				System.out.printf("Main: Removed: %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():pollFirst()和pollLast()方法分别返回双端队列的第一个和最后一个元素,删除从双端队列中返回的元素。如果双端队列为空,返回null值。
  • add()、addFirst()和addLast():addFirst()和addLast()分别在双端队列的第一个和最后一个位置添加一个元素。如果队列已满(创建时固定大小),抛出IllegalStateException异常。

更多关注

  • 本章“使用非阻塞线程安全双端队列”小节
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值