并发集合(二)使用非阻塞线程安全的列表

简介:

声明:本文是《 Java 7 Concurrency Cookbook 》的第六章,作者: Javier Fernández González     译者:许巧辉

使用非阻塞线程安全的列表

列表(list)是最基本的集合。一个列表有不确定的元素数量,并且你可以添加、读取和删除任意位置上的元素。并发列表允许不同的线程在同一时刻对列表的元素进行添加或删除,而不会产生任何数据不一致(问题)。

在这个指南中,你将学习如何在你的并发应用程序中使用非阻塞列表。非阻塞列表提供这些操作:如果操作不能立即完成(比如,你想要获取列表的元素而列表却是空的),它将根据这个操作抛出异常或返回null值。Java 7引进实现了非阻塞并发列表的ConcurrentLinkedDeque类。

我们将使用以下两种不同任务来实现一个例子:

  • 大量添加数据到列表
  • 在同个列表中,大量删除数据

准备工作…

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

如何做…

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

1.创建一个实现Runnable接口的AddTask类:

public class AddTask implements Runnable {

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

private ConcurrentLinkedDeque<String> list;

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

public AddTask(ConcurrentLinkedDeque<String> list) {
this.list=list;
}

4.实现这个类的run()方法。它将在列表中存储10000个正在执行任务的线程的名称和一个数字的字符串。

@Override
public void run() {
String name=Thread.currentThread().getName();
for (int i=0; i<10000; i++){
list.add(name+": Element "+i);
}
}

5.创建一个实现Runnable接口的PollTask类。

public class PollTask implements Runnable {

6.声明一个私有的、ConcurrentLinkedDeque类型的、参数化为String类的属性list。

private ConcurrentLinkedDeque<String> list;

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

public PollTask(ConcurrentLinkedDeque<String> list) {
this.list=list;
}

8.实现这个类的run()方法。它从列表中取出10000个元素(在一个循环5000次的循环中,每次取出2个元素)。

@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.创建一个参数化为String、名为list的ConcurrentLinkedDeque对象。

ConcurrentLinkedDeque<String> list=new ConcurrentLinkedDeque<>();

11.创建一个存储100个Thread对象的数组threads。

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对象来处理非阻塞并发列表的数据。以下截图显示这个例子执行的输出:

1

首先,你已执行100个AddTask任务来给列表添加元素。每个任务使用add()方法添加10000个元素到列表。这个方法将新元素插入到列表的尾部。当这些任务全部完成,你已在控制台打印这个列表元素的数量。此时,列表有1000000个元素。

然后,你执行100个PollTask任务从列表中删除元素。每个任务使用pollFirst()和pollLast()方法删除列表的10000个元素。pollFirst()方法返回并删除列表的第一个元素,pollLast()方法返回并删除列表的最后一个元素。如果列表为空,这些方法将返回一个null值。当这些任务全部完成,你已在控制台打印这个列表元素的数量。此时,列表有0个元素。

你使用size()方法,打印列表元素的数量。你必须考虑到这个方法会返回一个并不真实的值,尤其是如果你使用这个方法,而有线程正在添加或删除列表的数据。这个方法必须遍历整个列表来计算元素而对于这个操作列表的内容可以改变。只有在没有任何线程修改列表的情况下,你使用这个方法时,你将保证这个返回值是正确的。

不止这些…

ConcurrentLinkedDeque类提供更多方法来获取列表的元素:

  • getFirst()和getLast():这些方法将分别返回列表的第一个和最后一个元素。它们不会从列表删除返回的元素。如果列表为空,这些方法将抛出NoSuchElementExcpetion异常。
  • peek()、peekFirst()和peekLast():这些方法将分别返回列表的第一个和最后一个元素。它们不会从列表删除返回的元素。如果列表为空,这些方法将返回null值。
  • remove()、removeFirst()、 removeLast():这些方法将分别返回列表的第一个和最后一个元素。它们将从列表删除返回的元素。如果列表为空,这些方法将抛出NoSuchElementExcpetion异常。
  • 文章转自 并发编程网-ifeve.com
目录
相关文章
|
2月前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
221 0
|
1月前
|
安全
List并发线程安全问题
【10月更文挑战第21天】`List` 并发线程安全问题是多线程编程中一个非常重要的问题,需要我们认真对待和处理。只有通过不断地学习和实践,我们才能更好地掌握多线程编程的技巧和方法,提高程序的性能和稳定性。
190 59
|
25天前
|
安全 Java
线程安全的艺术:确保并发程序的正确性
在多线程环境中,确保线程安全是编程中的一个核心挑战。线程安全问题可能导致数据不一致、程序崩溃甚至安全漏洞。本文将分享如何确保线程安全,探讨不同的技术策略和最佳实践。
35 6
|
29天前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
52 6
|
28天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
1月前
|
开发框架 Java .NET
.net core 非阻塞的异步编程 及 线程调度过程
【11月更文挑战第12天】本文介绍了.NET Core中的非阻塞异步编程,包括其基本概念、实现方式及应用示例。通过`async`和`await`关键字,程序可在等待I/O操作时保持线程不被阻塞,提高性能。文章还详细说明了异步方法的基础示例、线程调度过程、延续任务机制、同步上下文的作用以及如何使用`Task.WhenAll`和`Task.WhenAny`处理多个异步任务的并发执行。
|
1月前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
1月前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
2月前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
34 1
|
3月前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
72 0