Java并发(五):并发,迭代器和容器
在随后的博文中我会继续分析并发包源码,在这里,得分别谈谈容器类和迭代器及其源码,虽然很突兀,但我认为这对于学习Java并发很重要;
ConcurrentModificationException:
JavaAPI中的解释:当不允许这样的修改时,可以通过检测到对象的并发修改的方法来抛出此异常。一个线程通常不允许修改集合,而另一个线程正在遍历它。 一般来说,在这种情况下,迭代的结果是未定义的。 某些迭代器实现(包括由JRE提供的所有通用集合实现的实现)可能会选择在检测到此行为时抛出此异常。 这样做的迭代器被称为"及时失败"迭代器,当他们发现容器在迭代时被修改时,就会报异常;它称不上时一种处理机制,而是一种预防机制,只能作为并发问题的预警指示器.
迭代器与容器:
Vector这个"古老"的容器类相信大家很熟悉了,他是线程安全的没错,我们利用elements()方法遍历,不会出现线程安全的问题:
/**
* JDK1.8源码
*/ /**
* Returns an enumeration of the components of this vector. The
* returned {@code Enumeration} object will generate all items in
* this vector. The first item generated is the item at index {@code 0},
* then the item at index {@code 1}, and so on.
*
* @return an enumeration of the components of this vector
* @see Iterator
*/
public Enumeration<E> elements() {
return new Enumeration<E>() {
int count = 0; public boolean hasMoreElements() {
return count < elementCount;
} public E nextElement() {
synchronized (Vector.this) {
if (count < elementCount) {
return elementData(count++);
}
}
throw new NoSuchElementException("Vector Enumeration");
}
};
}
JDK1.8源码: AbstractCollection (been extends by Vector)
但当我们利用foreach进行迭代时,底层自动调用了iterator(),若我们不锁住容器,可能会报ConcurrentModificationException
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
//JDK1.8写成了一个内部类的形式返回迭代器
} /**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount; Itr() {} public boolean hasNext() {
return cursor != size;
} //观察下面的代码可以发现,每次迭代一次都要检查容器长度是否改变
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
} public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
} @Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
} final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
JDK1.8源码: Vector
查看代码时可以发现,iterator()的内部类中提供的next,remove等方法都进行了迭代操作,这样的迭代被称为"隐藏迭代器";
部分容器类的toString()方法会在调用StringBuilder的append()同时迭代容器,例如继承了AbstractSet的HashSet类,而AbstractSet又继承于AbstractCollection:
// String conversion
/**
* Returns a string representation of this collection. The string
* representation consists of a list of the collection's elements in the
* order they are returned by its iterator, enclosed in square brackets
* (<tt>"[]"</tt>). Adjacent elements are separated by the characters
* <tt>", "</tt> (comma and space). Elements are converted to strings as
* by {@link String#valueOf(Object)}.
*
* @return a string representation of this collection
*/
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
JDK1.8源码: AbstractCollection
隐藏的迭代器普遍存在,甚至出现在hashCode,equals,contains,remove等方法中,此处不再赘述;
解决办法:
考虑到并发安全性,我们不得不对容器类单独加锁,但同时我们又不希望加锁,那样太损耗性能了,还存在线程饥饿和死锁的风险,极大降低吞吐量和CPU利用率;
作为有限的替代,我们可以考虑"克隆"容器,并在将其副本封闭起来进行迭代,避免了抛出ConcurrentModificationException,但在克隆过程中仍然要对容器加锁;显然,克隆容器存在系统开销,我们在选择这种方案时必须考虑的因素有:容器大小,每个元素上执行的工作,迭代操作相对于其它操作的调用频率,以及相应时间和系统吞吐率的需求,具体应用详见CopyOnWriteArrayList类.
参考材料:
Java并发编程实战
Java并发(五):并发,迭代器和容器的更多相关文章
- java 并发(五)---AbstractQueuedSynchronizer(5)
问题 : ArrayBlockQueue 和 LinkedBlockQueue 的区别 两者的实现又是怎么样的 应用场景 BlockingQueue 概述 blockingQueue 是个接口,从名字 ...
- java 并发(五)---AbstractQueuedSynchronizer
文章部分图片和代码来自参考文章. LockSupport 和 CLH 和 ConditionObject 阅读源码首先看一下注解 ,知道了大概的意思后,再进行分析.注释一开始就进行了概括.AQS的实现 ...
- JAVA多线程和并发基础面试问答(转载)
JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...
- [转] JAVA多线程和并发基础面试问答
JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...
- JAVA多线程和并发基础面试问答
转载: JAVA多线程和并发基础面试问答 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对 ...
- 【多线程】JAVA多线程和并发基础面试问答(转载)
JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...
- java处理高并发高负载类网站的优化方法
java处理高并发高负载类网站中数据库的设计方法(java教程,java处理大量数据,java高负载数据) 一:高并发高负载类网站关注点之数据库 没错,首先是数据库,这是大多数应用所面临的首个SPOF ...
- (转)JAVA多线程和并发基础面试问答
JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...
- Java编程思想 - 并发
前言 Q: 为什么学习并发? A: 到目前为止,你学到的都是有关顺序编程的知识,即程序中的所有事物在任意时刻都只能执行一个步骤. A: 编程问题中相当大的一部分都可以通过使用顺序编程来解决,然而,对于 ...
随机推荐
- Web Form要“jquery”ScriptResourceMapping。请添加一个名为 jquery (区分大小写)的 ScriptResourceMapping。”的解决办法。
1.先将aspnet.scriptmanager.jquery.dl 复制到bin (网站根目录下的bin文件夹找不到,看看下面的图片中点击[显示所有文档]) 文件夹下. 2.在网站根目录下s ...
- JsonPath的用法
有时候需要从json里面提取相关数据,必须得用到如何提取信息的知识,下面来写一下 语法格式 JsonPath 描述 $ 根节点 @ 当前节点 .or[] 子节点 .. 选择所有符合条件的节点 * 所有 ...
- Python版的数据库查询构造器、ORM及动态迁移数据表。
Orator Orator提供一个简单和方便的数据库数据处理库. 它的灵感来源于PHP的Laravel框架,借助其思想实现了python版的查询构造器和ORM. 这是完整的文档:http://orat ...
- MYSQL MYSQLI PDO
PHP的MySQL扩展(优缺点) 设计开发允许PHP应用与MySQL数据库交互的早期扩展.mysql扩展提供了一个面向过程 的接口: 并且是针对MySQL4.1.3或更早版本设计的.因此,这个扩展虽然 ...
- Exception异常处理
1.java异常类: 都是Throwable的子类: 1.Exception(异常) :是程序本身可以处理的异常. 2.Error(错误): 是程序无法处理的错误.这些错误表示故障发生于虚拟机自身.或 ...
- Spark Streaming 官网上提到的几点调优
总的来说,需要考虑以下两点: 1. 有效地运用集群资源去减少每个批次处理的时间 2. 正确的设置batch size,以使得处理速度能跟上接收速度 一. 为了减少处理时间,主要有以下几个优化点: 1 ...
- JavaScript 性能优化
加载和执行 1. </body>闭合标签之前,将所有的<script> 标签放在页面底部,确保在脚步执行之前页面已经完成渲染. 2. 合并脚本.下载单个 100KB 的文件将比 ...
- 用cmd命令来 启动和关闭 SqlServer
cmd命令 启动and关闭 sql数据库服务 (1)先用以管理员身份打开你的cmd窗口. (2)后输入以下对应的命名. 如下: 启动SQLSERVER NET START MSSQLSERVER 暂停 ...
- rem字体在rem盒子里面不一样,或者不同的行解决
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 根据从redis缓存的数据查询出来,在从数据库中取出所有的数据,俩个数据进行比较,去掉重复,剩下库中新插入的数据,取出新数据,然后把redis中的缓存数据清空把从数据库中查出来的所有数据放到redis缓存中
参考代码: public String getNewCenter(HttpServletRequest request,HttpServletResponse resonse){ JSONObject ...