[本文是我对Java Concurrency In Practice 5.1的归纳和总结. 转载请注明作者和出处, 如有谬误, 欢迎在评论中指正. ]
synchronized集合
java集合框架提供了多种synchronized集合, 比如Vector, HashTable, Collections的synchronizedXxx方法的返回值等.
synchronized集合是线程安全的, 但不是严格线程安全的. 根据JCIP第二章关于线程安全的定义--线程安全的类无需调用方进行额外的同步--synchronized集合是不满足该定义的. 如果我们将线程安全的定义放宽一些--单次调用对象的方法而无需调用方进行额外的同步, 这样synchronized集合就符合定义了.
为什么要加上单次调用的限定呢? 先看一个例子:
public static Object getLast(Vector list) {
int lastIndex = list.size() - 1;
return list.get(lastIndex);
}
public static void deleteLast(Vector list) {
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}
假设Vector对象中含有10个元素, 多线程环境下可能出现这样的场景:
线程1调用getLast方法, 计算得知lastIndex为9, 然后线程失去CPU使用权. 接着线程2调用deleteLast方法, 其lastIndex也为9, 线程2删除了第9个元素. 然后线程1重新获得CPU时间, 线程1会试图获取第9个元素, 但是该元素已经被线程2删除了, 此时将抛出ArrayIndexOutOfBoundsException异常.
从上面的例子可知, 尽管Vector对象是线程安全的, 但是如果对其进行复合操作的话(getLast方法既需要取得最后一个元素的索引, 还需要取得最后一个元素的值--类似这样的操作成为复合操作), 仍然需要调用方同步. 正确的做法应该是:
public static Object getLast(Vector list) {
synchronized (list) {
int lastIndex = list.size() - 1;
return list.get(lastIndex);
}
}
public static void deleteLast(Vector list) {
synchronized (list) {
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}
}
之所以用Vector对象做锁, 是因为Vector类的所有方法都是同步方法(其锁为this), 这样调用方使用的锁就同Vector内部使用的锁保持一致了.
迭代是最常见的复合操作, 迭代时调用方也需要进行额外的同步, 以保证整个迭代期间集合没有发生变化. 如:
List list = Collections.synchronizedList(new ArrayList());
// ...
synchronized (list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
ConcurrentModificationException异常和Fail-Fast机制
迭代集合时, 可能会发生ConcurrentModificationException异常. 每个集合内部都拥有一个名为modCount的成员变量, 如果集合发生了变化, 就会更改modCount的值. 使用Iterator开始迭代时,会将modCount的赋值给expectedModCount, 在迭代过程中, 通过每次比较两者是否相等来判断集合是否在内部或被其它线程修改. 如果expectedModCount和modCount不相等, 将抛出ConcurrentModificationException异常:
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
modCount被声明为volatile, 保证了线程可见性. 单线程环境下迭代时也有可能抛出ConcurrentModificationException异常, 比如在迭代时进行了会更改modCount值的操作:
Iterator<Integer> it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next());
// remove操作会导致modCount的值被修改, 从而引发ConcurrentModificationException异常
list.remove(0);
}
java集合类采用的这种机制被称为Fail-Fast机制--检查状态, 如果没有问题则忽略, 如果有问题就抛出异常. java集合采用这种方式避免同步所带来的开销, 如果确实需要同步, 可以使用synchronized集合, 或者在调用方进行, 或者先同步的clone一份集合然后对clone集合进行迭代--开发者可以进行选择. 使用clone的方式进行迭代的例子:
ArrayList<Integer> listClone = null;
// clone时依然需要同步
synchronized (list) {
listClone = (ArrayList<Integer>) list.clone();
}
Iterator<Integer> it = listClone.iterator();
while (it.hasNext()) {
doSomething(it.next());
}
采用clone的方式进行迭代是否合适, 取决于很多因素. 比如集合的size, doSomething的调用时间, 迭代的相对频率等. 如果doSomething方法的调用时间很长, 那么使用clone是比较合适的.
除了显式迭代之外, 调用集合的toString, containsAll, removeAll和retainAll等方法也会导致迭代发生.
concurrent集合
Collections.synchronizedXxx方法返回synchronized集合, 查看源码可知, 返回的Synchronized集合对象只是一个包装者, 其对每一个方法都进行同步. 如:
class SynchronizedList<E> extends SynchronizedCollection<E> implements List<E> {
static final long serialVersionUID = -7754090372962971524L;
final List<E> list;
SynchronizedList(List<E> list) {
super(list);
this.list = list;
}
SynchronizedList(List<E> list, Object mutex) {
super(list, mutex);
this.list = list;
}
public boolean equals(Object o) {
synchronized (mutex) {
return list.equals(o);
}
}
public int hashCode() {
synchronized (mutex) {
return list.hashCode();
}
}
public E get(int index) {
synchronized (mutex) {
return list.get(index);
}
}
// ....
}
这样的做法对性能有很大的影响, 比如多个线程的并发读操作并不会引起并发错误, 但在Synchronized集合中只能一个线程一个线程的读. 除此之外, javadoc明确要求使用Collections.synchronizedXxx包装一个集合对象后, 就不应该使用原有的集合, 因为使用原有的集合会破坏线程安全. 这样的线程安全依赖于约定, 是不可靠的.
jdk5及后续的版本增加了ConcurrentHashMap, CopyOnWriteArrayList, BlockingQueue, ConcurrentSkipListMap, ConcurrentSkipListSet等concurrent集合类.
分享到:
相关推荐
6. **并发集合**:Java提供了线程安全的集合,如`ConcurrentHashMap`、`CopyOnWriteArrayList`等,这些集合在并发环境中性能优异。书中会分析它们的设计原理和使用场景。 7. **线程池**:`ExecutorService`是线程池...
Java Concurrency in Practice 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者...
《Java Concurrency in Practice》是Java并发编程领域的一本经典著作,由Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowles和Doug Lea等专家共同编写。这本书深入探讨了Java平台上的多线程和并发编程,旨在...
<<java并行编程>>英文版chm格式,英文名称<Java Concurrency in Practice>,一直想买这本书,但总是缺货,找到了电子版,分享给大家。 Java Concurrency in Practice By Brian Goetz, Tim Peierls, Joshua Bloch,...
Using the concurrency building blocks in java.util.concurrent Performance optimization dos and don'ts Testing concurrent programs Advanced topics such as atomic variables, nonblocking algorithms, ...
Java Concurrency in practice
Java Concurrency in Practice JAVA并发编程实践中文版(全)第二部分
这里的"java_concurrency_in_practice_source"源代码正是书中实例的实现,它涵盖了Java多线程编程中的关键概念和技术。 1. **线程基础**:Java中创建线程有两种方式,一是通过`Thread`类的子类,二是实现`Runnable`...
java concurrency in practice 经典的多线程编程书籍,英文版
《Java Concurrency In Practice》是一本关于Java并发编程的经典著作,由Brian Göetz、Tim Peierls、Joshua Bloch、Joseph Bowbeer、David Holmes和Doug Lea共同编写。本书深入探讨了Java平台上的多线程编程技巧,...
本笔记将深入探讨《Java Concurrency In Practice》这本书中的核心概念,结合Guava库的实际使用案例,帮助读者理解并掌握Java并发编程的精髓。 首先,我们来了解Java并发的基础知识。Java提供了丰富的并发工具类,...
- **书名**:《Java并发实践》(Java Concurrency in Practice) - **作者**:Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea - **出版社**:Addison Wesley Professional - **...
首先,"Java Concurrency in Practice"是Java并发编程的经典之作,由Brian Goetz、Tim Peierls、Joshua Bloch、David Holmes和Doug Lea合著。这本书提供了一套实用的指导原则、设计模式和最佳实践,帮助Java开发者...
java_concurrency_in_practice.pdf jcip-examples-src.jar jcip-annotations-src.jar 英文版是高清晰的,实战和实践都是同一帮人对英文版书的翻译,网传实战的翻译质量更好,实战是2012年出版的,应该是对前一版实践...
这本书很出名吧,大家都知道吧,我靠,20个字的描述咋这么累啊。