在java中,最常用的數據結構可能是列表。有數目不詳的元素列表,你可以添加、閱讀、或刪除任何位置的元素。此外,並發列表允許不同的線程列表中添加或刪除元素時不產生任何數據不一致。非阻塞列表提供如下操作,如果操作不能立即完成,列出拋出異常或者返回一個null值。Java 7中引入了ConcurrentLinkedDeque類,它實現了一個非阻塞並發列表,在本教程中,我們將學習使用這個類。
在這個例子中,我們將實現一個示例使用以下兩個不同的任務:
一個將大量數據添加到一個列表中
一個大量地從同樣的列表中刪除數據
讓我們為每個任務創建的線程:
package com.howtodoinjava.demo.multithreading.concurrentLinkedDequeExample; import java.util.concurrent.ConcurrentLinkedDeque; public class AddTask implements Runnable { private ConcurrentLinkedDequelist; public AddTask(ConcurrentLinkedDeque 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); } } }
和:
package
com.howtodoinjava.demo.multithreading.concurrentLinkedDequeExample;
import
java.util.concurrent.ConcurrentLinkedDeque;
public
class
RemoveTask
implements
Runnable {
private
ConcurrentLinkedDeque list;
public
RemoveTask(ConcurrentLinkedDeque list) {
this
.list = list;
}
@Override
public
void
run() {
for
(
int
i =
0
; i <
5000
; i++) {
list.pollFirst();
list.pollLast();
}
}
}
現在,讓我們創建100個線程將數據添加到列表和100個線程從列表刪除數據。如果真的是線程安全的和非阻塞,它會幾乎立即給你最終結果。此外,列表大小最終將是零。
package com.howtodoinjava.demo.multithreading.concurrentLinkedDequeExample; import java.util.concurrent.ConcurrentLinkedDeque; public class Main { public static void main(String[] args) { ConcurrentLinkedDequelist = 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 , 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 , 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 , 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 , 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
讓我們看看它如何工作:
AddTask
將元素添加到任務列表。每一個這些任務插入10000個元素的列表使用 add()
方法。這種方法增加了新元素列表的最後。當所有這些任務已經完成了,你寫在控制台的數量列表的元素。這時,有1000000個元素的列表。 RemoveTask
任務來從列表中刪除元素。每一個這些任務刪除使用10000個元素的列表 pollFirst()
和pollLast()
方法。pollFirst()方法返回和刪除列表的第一個元素和pollLast()方法返回和刪除最後一個元素的列表。如果列表為空,這些方法返回一個null值。當所有這些任務已經完成了,你寫在控制台的數量列表的元素。這時,有零元素列表。 size()
方法。你必須考慮,這種方法可以返回一個值,並不是真實的,特別是如果你使用它當線程列表中添加或刪除數據。計數的方法遍歷整個列表的元素和內容列表可以改變這個操作。只有在你使用它們時沒有任何線程修改列表,你可以保證返回的結果是正確的。
請注意, ConcurrentLinkedDeque
類提供了更多的方法來獲取元素列表形式:
getFirst()
和 getLast()
:這些方法返回分別從列表中第一個和最後一個元素。他們不會從列表中刪除返回的元素。如果列表是空的,這些方法拋出一個 NoSuchElementExcpetion
例外。peek()
, peekFirst()
, peekLast()
:這些方法返回列表的第一個和最後一個元素。他們不會從列表中刪除返回的元素。如果列表為空,這些方法返回一個null值。remove()
, removeFirst()
, removeLast()
:這些方法返回列表的第一個和最後一個元素。他們從列表中移除返回的元素。如果列表是空的,這些方法拋出一個 NoSuchElementException
例外。 ConcurrentLinkedDeque
是一個合適的選擇,許多線程共享訪問公共集合。ConcurrentModificationException
與其他操作,可以同時進行。