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

Java 9并发编程指南 目录

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

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

本节将学习在并发应用中如何使用非阻塞双端队列。非阻塞双端队列提供的操作如果没有马上执行完(例如想要得到空列表中的一个元素),将根据操作不同抛出异常或者返回null值。Java 7引入ConcurrentLinkedDeque类实现非阻塞并发双端队列。

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

  • 向双端队列添加大量元素
  • 从双端队列中删除数据

准备工作

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

实现过程

通过如下步骤实现范例:

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

    public class AddTask implements Runnable{
    
  2. 声明名为list的String类参数化的私有ConcurrentLinkedDeque属性:

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

    	public AddTask(ConcurrentLinkedDeque<String> list) {
    		this.list = list;
    	}
    
  4. 实现类的run()方法,循环10000次,在每个循环中,获取双端队列的第一个和最后一个元素,所以将得到总计10000个元素:

    	@Override
    	public void run() {
    		String name=Thread.currentThread().getName();
    		for (int i=0; i<10000; i++){
    			list.add(name+": Element "+i);
    		}
    	}
    
  5. 创建名为PollTask的类,指定其实现Runnable接口:

    public class PollTask implements Runnable{
    
  6. 声明名为list的String类参数化的私有ConcurrentLinkedDeque属性:

    	private final ConcurrentLinkedDeque<String> list;
    
  7. 实现类构造函数,初始化属性:

    	public PollTask(ConcurrentLinkedDeque<String> list) {
    		this.list = list;
    	}
    
  8. 实现类的run()方法,通过循环5000次取出双端队列10000个元素,每次取头和尾两个元素:

    	@Override
    	public void run() {
    		for (int i=0; i<5000; i++) {
    			list.pollFirst();
    			list.pollLast();
    		}
    	}
    
  9. 通过创建名为Main的类,添加main()方法,实现本范例主类:

    public class Main {
    	public static void main(String[] args) {
    
  10. 创建名为list的String类参数化的私有ConcurrentLinkedDeque属性:

    		ConcurrentLinkedDeque<String> list=new ConcurrentLinkedDeque<>();
    
  11. 创建名为threads的数组,包含100个Thread对象:

    		Thread threads[]=new Thread[100];
    
  12. 创建100个AddTask对象以及运行这些对象的线程,将线程存储到已创建好的数组中并启动:

    		for (int i=0; i<threads.length ; i++){
    			AddTask task=new AddTask(list);
    			threads[i]=new Thread(task);
    			threads[i].start();
    		}
    		System.out.printf("Main: %d AddTask threads have been launched\n",threads.length);
    
  13. 使用join()方法等待线程执行完成:

    		for (int i=0; i<threads.length; i++) {
    			try {
    				threads[i].join();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    
  14. 输出列表长度到控制台:

    		System.out.printf("Main: Size of the List: %d\n",list.size());
    
  15. 创建100个PollTask对象以及运行这些对象的线程,将线程存储到已创建好的数组中并启动:

    		for (int i=0; i< threads.length; i++){
    			PollTask task=new PollTask(list);
    			threads[i]=new Thread(task);
    			threads[i].start();
    		}
    		System.out.printf("Main: %d PollTask threads have been launched\n", threads.length);
    
  16. 使用join()方法等待线程执行完成:

    		for (int i=0; i<threads.length; i++) {
    			try {
    				threads[i].join();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    
  17. 输出列表长度到控制台:

    		System.out.printf("Main: Size of the List: %d\n",list.size());
    	}
    }
    

工作原理

在本节中,使用String类参数化的ConcurrentLinkedDeque对象来处理数据的非阻塞并发双端队列。下图显示本范例在控制台输出的执行信息:

pics/07_01.jpg

首先,执行100个AddTask任务来添加元素到列表,每个任务都使用add()方法向列表中插入10000个元素,此方法在双端队列尾部添加新元素。当所有任务完成时,输出双端队列的元素总数到控制台,此时双端队列有1000000个元素。

然后,执行100个PollTGask任务从双端队列中删除元素。每个任务都使用pollFirst()和pollLast()方法从双端队列中删除10000个元素。pollFirst()方法返回和删除双端队列的第一个元素,pollLast()方法返回和删除双端队列的最后一个元素。如果双端队列为空,则返回null值。当所有任务完成时,输出双端队列的元素总数到控制台,此时考虑到ConcurrentLinkedDeque结构不允许列表添加null值,列表具有零个元素。

使用size()方法输出双端队列的元素数量到控制台,需要注意的是此方法返回的值不是准确的,尤其当有线程在列表中添加或删除数据时使用此方法。这个方法必须遍历整个双端队列来计算元素数量,并且此操作能够更改列表的内容。 只有当没有线程修改双端队列时使用此方法,才能保证返回的结果是正确的。

扩展学习

ConcurrentLinkedDeque类还有很多从双端队列中获取元素的方法:

  • getFirst()和getLast():这两个方法分别返回双端队列的第一个和最后一个元素,不删除从双端队列中返回的元素。如果双端队列为空,则抛出NoSuchElementExcpetion异常。
  • peek()、peekFirst()和peekLast() :这些方法分别返回双端队列的第一个和最后一个元素,不删除从双端队列中返回的元素。如果双端队列为空,返回null值。
  • remove()、removeFirst()和removeLast():这些方法分别返回双端队列的第一个和最后一个元素,删除从双端队列中返回的元素。如果双端队列为空,则抛出NoSuchElementExcpetion异常。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值