并发集合(九)使用原子 arrays

当你实现一个多个线程共享一个或者多个对象的并发应用时,你就要使用像锁或者同步关键词(例如synchronized)来对他们的属性的访问进行保护,来避免并发造成的数据不一致的错误。

但是这些机制会有以下一些缺点:
死锁(dead lock):例如:当一个线程等待一个锁的时候,会被阻塞,而这个锁被其他线程占用并且永不释放。这种情况就是死锁,程序在这种情况下永远都不会往下执行。

即使只有一个线程在访问共享对象,它也要执行必要的获取锁和释放锁的代码。

CAS(compare-and-swap)操作为并发操作对象的提供更好的性能,CAS操作通过以下3个步骤来实现对变量值得修改:

  1. 获取当前内存中的变量的值
  2. 用一个新的临时变量(temporal variable)保存改变后的新值
  3. 如果当前内存中的值等于变量的旧值,则将新值赋值到当前变量;否则不进行任何操作

对于这个机制,你不需要使用任何同步机制,这样你就避免了 deadlocks,也获得了更好的性能。这种机制能保证多个并发线程对一个共享变量操作做到最终一致。

Java 在原子类中实现了CAS机制。这些类提供了compareAndSet() 方法;这个方法是CAS操作的实现和其他方法的基础。

Java 中还引入了原子Array,用来实现Integer类型和Long类型数组的操作。在这个指南里,你将要学习如何使用AtomicIntegerArray 类来操作原子 arrays。

指南中的例子是在Eclipse IDE下面实现的,你也可以使用其他IDE例如NetBeans来实现:

那要怎么做呢….

按照这些步骤来实现下面的例子:

01 //1.创建一个类,名为 Incrementer,并实现 Runnable 接口。
02 public class Incrementer implements Runnable {
03  
04 //2.声明一个私有 AtomicIntegerArray 属性,名为 vector,用来储存一个整数 array。
05 private AtomicIntegerArray vector;
06  
07 //3.实现类的构造函数,初始化它的属性值。
08 public Incrementer(AtomicIntegerArray vector) {
09          this.vector=vector;
10 }
11  
12 //4.实现 run() 方法。使用 getAndIncrement() 方操作array里的所有元素。
13 @Override
14 public void run() {
15          for (int i=0; i<vector.length(); i++){
16                  vector.getAndIncrement(i);
17          }
18 }
19  
20 //5.创建一个类,名为 Decrementer,并实现 Runnable 接口。
21 public class Decrementer implements Runnable {
22  
23 //6.声明一个私有 AtomicIntegerArray 属性,名为 vector,用来储存一个整数 array。
24 private AtomicIntegerArray vector;
25  
26 //7.实现类的构造函数,初始化它的属性值。
27 public Decrementer(AtomicIntegerArray vector) {
28          this.vector=vector;
29 }
30  
31 //8.实现 run() 方法。使用 getAndDecrement() 方法操作array里的所有元素。
32 @Override
33 public void run() {
34          for (int i=0; i<vector.length(); i++) {
35          vector.getAndDecrement(i);
36          }
37 }
38  
39 //9.我们创建一个示例来进行示范,创建一个类,名为 Main 并添加 main()方法。
40 public class Main {
41          public static void main(String[] args) {
42  
43 //10.声明一个常量,名为 THREADS,分配它的值为 100。创建一个有1,000个元素的 AtomicIntegerArray 对象。
44 final int THREADS=100;
45 AtomicIntegerArray vector=new AtomicIntegerArray(1000);
46  
47 //11. 创建一个 Incrementer 任务来操作之前创建的原子 array。
48 Incrementer incrementer=new Incrementer(vector);
49  
50 //12.创建一个 Decrementer 任务来操作之前创建的原子 array。
51 Decrementer decrementer=new Decrementer(vector);
52  
53 //13.创建2个array 分别存储 100 个Thread 对象。
54 Thread threadIncrementer[]=new Thread[THREADS];
55 Thread threadDecrementer[]=new Thread[THREADS];
56  
57 //14.创建并运行 100 个线程来执行 Incrementer 任务和另外 100 个线程来执行 Decrementer 任务。把线程储存入之前创建的arrays内。
58 for (int i=0; i<THREADS; i++) {
59          threadIncrementer[i]=new Thread(incrementer);
60          threadDecrementer[i]=new Thread(decrementer);
61  
62          threadIncrementer[i].start();
63          threadDecrementer[i].start();
64 }
65 //15.使用 join() 方法来等待线程的完结。
66 for (int i=0; i<100; i++) {
67          try {
68                  threadIncrementer[i].join();
69                  threadDecrementer[i].join();
70          } catch (InterruptedException e) {
71                  e.printStackTrace();
72          }
73 }
74 //16.把原子array里非0的元素写入操控台。使用 get() 方法来获取原子 array 元素。
75 for (int i=0; i<vector.length(); i++) {
76          if (vector.get(i)!=0) {
77                  System.out.println("Vector["+i+"] : "+vector.get(i));
78          }
79 }
80  
81 //17.在操控台写个信息表明例子结束。
82 System.out.println("Main: End of the example");

它是怎么工作的…

在这个例子里,你实现了2个不同的任务来操作 AtomicIntegerArray 对象:

Incrementer task: 这个类使用getAndIncrement()方法array里的全部元素 +1
Decrementer task: 这个类使用getAndDecrement()方法array里的全部元素 -1

在 Main 类,你创建了有1000个元素的 AtomicIntegerArray,然后你执行了100次 Incrementer 和100次 Decrementer 任务。在任务结束后,如果没有出现任何数据不一致错误,那么array的全部元素的值都为0。如果你运行这个任务,由于全部元素都是0,你只会看到程序在操控台只写了结束信息。

更多…

如今,Java仅提供了另一个原子 array类。它是 AtomicLongArray 类,与 IntegerAtomicArray 类提供了相同的方法。

这些类的一些其他有趣的方法有:
get(int i): 返回array中第i个位置上的值
set(int I, int newValue): 设置array中第i个位置上的值为newValue

参见
第六章,并发集:使用原子变量

时间: 2025-01-02 03:24:46

并发集合(九)使用原子 arrays的相关文章

并发集合(六)使用线程安全的NavigableMap

使用线程安全的NavigableMap Java API 提供的有趣的数据结构,并且你可以在并发应用程序中使用,它就是ConcurrentNavigableMap接口的定义.实现ConcurrentNavigableMap接口的类存储以下两部分元素: 唯一标识元素的key 定义元素的剩余数据 每部分在不同的类中实现. Java API 也提供了这个接口的实现类,这个类是ConcurrentSkipListMap,它实现了非阻塞列表且拥有ConcurrentNavigableMap的行为.在内部实

C#常用算法:并发集合

微软对C#(4.0)的框架添加了全新的并发编程框架,现在我们也能用C#开发支持并发概念的程序的.在并发编程中最让人烦恼的应该就是如何数据同步:避免脏读和脏写,当然我们可以通过Lock技术来实现,也可以使用微软提供给我们的并发集合,这些集合都提供了TryDo方法.用它们对数据的读/写操作能在TryDo返回True的情况下执行.我们来看看它们吧: IProducerConsumerCollection 所有的并发集合都实现了这个接口,TryAdd和TryTake分别在读和写的时候判断是否能正常进行,

C#并行编程-并发集合

原文:C#并行编程-并发集合 菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 背景 基于任务的程序设计.命令式数据并行和任务并行都要求能够支持并发更新的数组.列表和集合. 在.NET Framework 4 以前,为了让共享的数组.列表和集合能够被多个线程更新,需要添加复杂的代码来同步这些更新操作. 如您需要编写一个并行循环,这个循环以无序的方式向一个共享集合中添加元素,那么必须加入一个同步机制来保证这是一个线程安全的集合. System.Collenctions

并发集合(八)使用原子变量

声明:本文是< Java 7 Concurrency Cookbook>的第一章, 作者: Javier Fernández González 译者:郑玉婷 校对:方腾飞 在Java 1.5中就引入了原子变量,它提供对单个变量的原子操作.当你在操作一个普通变量时,你在Java实现的每个操作,在程序编译时会被转换成几个机器能读懂的指令.例如,当你分配一个值给变量,在Java你只使用了一个指令,但是当你编译这个程序时,这个指令就被转换成多个JVM 语言指令.这样子的话当你在操作多个线程且共享一个变

Java并发集合的实现原理

本文简要介绍Java并发编程方面常用的类和集合,并介绍下其实现原理. AtomicInteger 可以用原子方式更新int值.类 AtomicBoolean.AtomicInteger.AtomicLong 和 AtomicReference 的实例各自提供对相应类型单个变量的访问和更新.基本的原理都是使用CAS操作: boolean compareAndSet(expectedValue, updateValue); 如果此方法(在不同的类间参数类型也不同)当前保持expectedValue,

阿里巴巴 JAVA开发手册 内含大量规范,应用范例.涉及数据库,高并发,集合,索引等等大量干货

  阿里巴巴 JAVA开发手册 Java开发手册 版本号 制定团队 更新日期 备  注 1.0.0 阿里巴巴集团技术部 2016.12.7 首次向Java业界公开 一.编程规约 (一)命名规约 1.  [强制]所有编程相关命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束. 反例:  _name  /  __name  /  $Object  /   name_  /  name$  /  Object$ 2.  [强制]所有编程相关的命名严禁使用拼音与英文混合的方式,更不允许直接使

《Java 7并发编程实战手册》第六章并发集合

由人民邮电出版社出版的<Java 7并发编程实战手册>终于出版了,译者是俞黎敏和申绍勇,该书将于近期上架.之前并发编程网组织翻译过此书,由于邮电出版社在并发网联系他们之前就找到了译者,所以没有采用并发网的译稿,但邮电出版社将于并发网展开合作,发布该书的样章(样章由并发网挑选,你也可以回帖告诉我们你想看哪一章的样章),并组织赠书活动回馈给活跃读者.活动详情请时刻关注并发网的微博和微信(微信号:ifeves),最后祝各位用餐愉快!:) 本章将介绍下列内容: 使用非阻塞式线程安全列表 使用阻塞式线程

并发集合(一)引言

声明:本文是< Java 7 Concurrency Cookbook >的第六章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞 在本章中,我们将包含: 使用非阻塞线程安全的列表 使用阻塞线程安全的列表 用优先级对使用阻塞线程安全的列表排序 使用线程安全的.带有延迟元素的列表 使用线程安全的NavigableMap 创建并发随机数 使用原子变量 使用原子数组 引言 在编程中,数据结构是一种基本的元素.几乎每个程序都使用一个或多个数据结构类型来存

Oracle官方并发教程之并发集合

原文地址 译文地址  译者:李任  校对:方腾飞 java.util.concurrent包囊括了Java集合框架的一些附加类.它们也最容易按照集合类所提供的接口来进行分类: BlockingQueue定义了一个先进先出的数据结构,当你尝试往满队列中添加元素,或者从空队列中获取元素时,将会阻塞或者超时. ConcurrentMap是java.util.Map的子接口,定义了一些有用的原子操作.移除或者替换键值对的操作只有当key存在时才能进行,而新增操作只有当key不存在时.使这些操作原子化,可