在java中,最常用的数据结构可能是列表。有数目不详的元素列表,你可以添加、阅读、或删除任何位置的元素。此外,并发列表允许不同的线程列表中添加或删除元素时不产生任何数据不一致。非阻塞列表提供如下操作,如果操作不能立即完成,列出抛出异常或者返回一个null值。Java 7中引入了ConcurrentLinkedDeque类,它实现了一个非阻塞并发列表,在本教程中,我们将学习使用这个类。
在这个例子中,我们将实现一个示例使用以下两个不同的任务:
一个将大量数据添加到一个列表中
一个大量地从同样的列表中删除数据
让我们为每个任务创建的线程:
package com.howtodoinjava.demo.multithreading.concurrentLinkedDequeExample; import java.util.concurrent.ConcurrentLinkedDeque; public class AddTask implements Runnable { private ConcurrentLinkedDeque<String> list; public AddTask(ConcurrentLinkedDeque<String> list) { this.list = list; } @Override public void run() { String name = Thread.currentThread().getName(); for (int i = 0; i < 10000; i++) { list.add(name + ": Element " + i); } } }
和:
|
现在,让我们创建100个线程将数据添加到列表和100个线程从列表删除数据。如果真的是线程安全的和非阻塞,它会几乎立即给你最终结果。此外,列表大小最终将是零。
package com.howtodoinjava.demo.multithreading.concurrentLinkedDequeExample; import java.util.concurrent.ConcurrentLinkedDeque; public class Main { public static void main(String[] args) { ConcurrentLinkedDeque<String> list = new ConcurrentLinkedDeque<>(); Thread threads[] = new Thread[100]; 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); for (int i = 0; i < threads.length; i++) { try { threads[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.printf("Main: Size of the List: %d\n", list.size()); for (int i = 0; i < threads.length; i++) { RemoveTask task = new RemoveTask(list); threads[i] = new Thread(task); threads[i].start(); } System.out.printf("Main: %d RemoveTask threads have been launched\n", threads.length); for (int i = 0; i < threads.length; i++) { try { threads[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.printf("Main: Size of the List: %d\n", list.size()); } } Output: Main: 100 AddTask threads have been launched Main: Size of the List: 1000000 Main: 100 RemoveTask threads have been launched Main: Size of the List: 0
让我们看看它如何工作:
- 首先,你有100执行
AddTask
将元素添加到任务列表。每一个这些任务插入10000个元素的列表使用add()
方法。这种方法增加了新元素列表的最后。当所有这些任务已经完成了,你写在控制台的数量列表的元素。这时,有1000000个元素的列表。 - 然后,你有100执行
RemoveTask
任务来从列表中删除元素。每一个这些任务删除使用10000个元素的列表pollFirst()
和pollLast()
方法。pollFirst()方法返回和删除列表的第一个元素和pollLast()方法返回和删除最后一个元素的列表。如果列表为空,这些方法返回一个null值。当所有这些任务已经完成了,你写在控制台的数量列表的元素。这时,有零元素列表。 - 编写列表的元素的数量,你使用了
size()
方法。你必须考虑,这种方法可以返回一个值,并不是真实的,特别是如果你使用它当线程列表中添加或删除数据。计数的方法遍历整个列表的元素和内容列表可以改变这个操作。只有在你使用它们时没有任何线程修改列表,你可以保证返回的结果是正确的。
请注意, ConcurrentLinkedDeque
类提供了更多的方法来获取元素列表形式:
getFirst()
和getLast()
:这些方法返回分别从列表中第一个和最后一个元素。他们不会从列表中删除返回的元素。如果列表是空的,这些方法抛出一个NoSuchElementExcpetion
例外。peek()
,peekFirst()
,peekLast()
:这些方法返回列表的第一个和最后一个元素。他们不会从列表中删除返回的元素。如果列表为空,这些方法返回一个null值。remove()
,removeFirst()
,removeLast()
:这些方法返回列表的第一个和最后一个元素。他们从列表中移除返回的元素。如果列表是空的,这些方法抛出一个NoSuchElementException
例外。- 一个
ConcurrentLinkedDeque
是一个合适的选择,许多线程共享访问公共集合。 - 像大多数其他并发集合实现,这个类不允许null元素的使用。
- 迭代器是弱一致的,返回元素反映在一些点或双端队列的状态,因为迭代器的创建。他们不把
ConcurrentModificationException
与其他操作,可以同时进行。
快乐学习!
!
原文链接:http://howtodoinjava.com/2015/02/23/non-blocking-thread-safe-list-concurrentlinkeddeque-example/