- 浏览: 144796 次
-
最新评论
-
zhh0000zhh:
用1W的线程跑epoll,别误人子弟了行不
freebsd里的kqueue 和linux 2.6下的epoll -
shiguanghui:
拼命的转载。你都没试过,别人的疑问你也不回答。你转来转去只是制 ...
oracle rownum与order by -
programdolt:
我最终验证了,和索引建立时间先后没关系应该必须是“非空索引”, ...
oracle rownum与order by -
programdolt:
我感觉,索引必须提前建立,在数据插入前建立,这样才能符合楼主的 ...
oracle rownum与order by -
wenyizeng:
貌似还不错
ant 命令详解
1. java.util.concurrent所提供的并发容器
java.util.concurrent提供了多种并发容器,总体上来说有4类,队列类型的BlockingQueue和 ConcurrentLinkedQueue,Map类型的ConcurrentMap,Set类型的ConcurrentSkipListSet和CopyOnWriteArraySet,List类型的CopyOnWriteArrayList.
这些并发容器都采用了多种手段控制并发的存取操作,并且尽可能减小控制并发所带来的性能损耗。接下来我们会对每一种类型的实现类进行代码分析,进而得到java.util.con current包所提供的并发容器在传统容器上所做的工作。
2. BlockingQueue
BlockingQueue接口定义的所有方法实现都是线程安全的,它的实现类里面都会用锁和其他控制并发的手段保证这种线程安全,但是这些类同时也实现了Collection接口(主要是AbstractQueue实现),所以会出现BlockingQueue的实现类也能同时使用Conllection接口方法,而这时会出现的问题就是像addAll,containsAll,retainAll和removeAll这类批量方法的实现不保证线程安全,举个例子就是addAll 10个items到一个ArrayBlockingQueue,可能中途失败但是却有几个item已经被放进这个队列里面了。
下面我们根据这幅类图来逐个解析不同实现类的特性和特性实现代码
DelayQueue提供了一个只返回超时元素的阻塞队列,也就是说,即使队列中已经有数据了,但是poll或者take的时候还要判定这个element有没达到规定的超时时间,poll方法在element还没达到规定的超时时间返回null,take则会通过condition.waitNanos()进入等待状态。一般存储的element类型为Delayed,这个接口JDK中实现的类有ScheduledFutureTask,而DelayQueue为DelayedWorkQueue的Task容器,后者是ScheduledThreadPoolExecutor的工作队列,所以DelayQueue所具有的超时提供元素和线程安全特性对于并发的定时任务有很大的意义。
final ReentrantLock lock = this.lock;
//控制并发
lock.lockInterruptibly();
try {
for (;;) {
E first = q.peek();
if (first == null) {
//condition协调队列里面元素
available.await();
} else {
long delay = first.getDelay(TimeUnit.NANOSECONDS);
if (delay > 0) {
//因为first在队列里面的delay最短的(优先队列保证),所以wait这个时间那么队列中最短delay的元素就超时了.即
//队列有元素供应了.
long tl = available.awaitNanos(delay);
} else {
E x = q.poll();
assert x != null;
if (q.size() != 0)
available.signalAll(); // wake up other takers
return x;
}
}
}
} finally {
lock.unlock();
}
}
PriorityBlockingQueue实现对于外部而言是按照元素的某种顺序返回元素,同时对存取提供并发保护(ReetranLock),使用Condition协调队列是否有新元素提供。PriorityBlocking Queue内部的数据结构为PriorityQueue,优先级排序工作交给PriorityQueue,至于怎么排序,需要根据插入元素的Comparable的接口实现,和DelayQueue比起来,它没有限定死插入数据的Comparable实现,而DelayQueue的元素实现Comparable必须按照超时时间的长短进行比较,否则DelayQueue返回的元素就很可能是错误的。
ArrayBlockingQueue是一个先入先出的队列,内部数据结构为一个数组,并且一旦创建这个队列的长度是不可改变的,当然put数据时,这个队列也不会自动增长。ArrayBlockingQueue也是使用ReetranLock来保证存取的原子性,不过使用了notEmpty和notFull两个Condition来协调队列为空和队列为满的状态转换,插入数据的时候,判定当前内部数据结构数组E[] items的长度是否等于元素计数,如果相等,说明队列满,notFull.await(),直到items数组重新不为满(removeAt,poll等),插入数据后notEmpty.sinal()通知所有取数据或者移除数据并且因为items为空而等待的线程可以继续进行操作了。提取数据或者移除数据的过程刚好相反。
ArrayBlockingQueue使用三个数字来维护队列里面的数据变更,包括takeIndex,putIndex,count,这里需要讲一下takeIndex和putIndex,其中takeIndex指向下一个能够被提取的元素,而putIndex指向下一个能够插入数据的位置,实现类似下图的结构,当takeIndex移到内部数组items最大长度时,重新赋值为0,也就是回到数组头部,putIndex也是相同的策略./**
* 循环增加putIndex和takeIndex,如果到数组尾部,那么
* 置为0
*/
final int inc(int i)
{
return (++i == items.length)? 0 : i;
}
//**
* 插入一个item,需要执行线程获得了锁
*/
private void insert(E x)
{
items[putIndex] = x;
//累加putIndex,可能到数组尾部,那么重新指向0位置
putIndex = inc(putIndex);
++count;
//put后,使用Condition通知正在等待take的线程可以做提取操作 notEmpty.signal();
}
/**
* 获取一个元素,执行这个操作的前提是线程已经获得锁,
* 内部调用
*/
private E extract()
{
final E[] items = this.items;
E x = items[takeIndex];
items[takeIndex] = null;
//累加takeIndex,有可能到数组尾部,重新调到数组头部
takeIndex = inc(takeIndex);
--count;
//take后,使用Condition通知正在等待插入的线程可以插入
notFull.signal();
return x;
}
public final void await() throws InterruptedException
{
if (Thread.interrupted())
throw new InterruptedException();
//加一个新的condition等待节点
Node node = addConditionWaiter();
//释放自己占用的锁
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node))
{
//如果当前线程等待状态是CONDITION,park住当前线程,等待condition的signal来解除
LockSupport.park(this);
if ((interruptMode =checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
private E dequeue()
{
Node<E> h = head;
Node<E> first = h.next;
//将原始节点的next指针指向自己,这样就能GC到自己否则虚拟机会认为这个节点仍然在用而不销毁(不知道是否理解有误)
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}
public E pollFirst() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return unlinkFirst();
} finally {
lock.unlock();
}
}
public E pollLast() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return unlinkLast();
} finally {
lock.unlock();
}
}
3. ConcurrentMap
ConcurrentMap定义了V putIfAbsent(K key,V value),Boolean remove(Object Key,Object value),Boolean replace(K key,V oldValue,V newValue)以及V replace(K key,V value)四个方法,几个方法的特性并不难理解,4个方法都是线程安全的。
ConcurrentHashMap是ConcurrentMap的一个实现类,这个类的实现相当经典,基本思想就是分拆锁,默认ConcurrentHashMap会实例化一个持有16个Segment对象的数组,Segment数组大小是可以设定的,构造函数里的concurrencyLevel指定这个值,但是需要注意的是,这个值并不是直接赋值.Segment数组最大长度为MAX_SEGMENTS = 1 << 16int sshift = 0;
int ssize = 1;
//ssize是左移位的,也就是2,4,8,16,32
增长(*2),所以你设定concurrencyLevel为10的时候,这个时候并发数最大为8.
while (ssize < concurrencyLevel)
{
++sshift;
ssize <<= 1;
}
int c = count;
//threshold一般(int)(capacity * loadFactor),
if (c++ > threshold)
rehash();
下面3段代码是ConcurrentHashMap的初始化Segment,计算hash值,以及如何选择Segment的代码以及示例注解.
public ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel)
{
if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
throw new IllegalArgumentException();
//首先确定segment的个数,左移位,并且记录移了几次,比如conurrencyLevel为30,那么2->4->8->16,ssize为16,sshift为4
if (concurrencyLevel > MAX_SEGMENTS)
concurrencyLevel = MAX_SEGMENTS;
int sshift = 0;
int ssize = 1;
while (ssize < concurrencyLevel)
{
++sshift;
ssize <<= 1;
}
//segmentShift为28
segmentShift = 32 - sshift;
//segmentMask为15
segmentMask = ssize - 1;
//this.segments=new Segment[16]
this.segments = Segment.newArray(ssize);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//假设initialCapacity使用32,那么c=2
int c = initialCapacity / ssize;
if (c * ssize < initialCapacity)
++c;
int cap = 1;
//cap为2
while (cap < c)
cap <<= 1;
//每个Segment的容量为2
for (int i = 0; i < this.segments.length; ++i)
this.segments[i] = new Segment<K,V>(cap, loadFactor);
}
/** *//**
*segmentShift为28,segmentMask为15(1111)
*因为hash值为int,所以32位的
*hash >>> segentShift会留下最高的4位,
*再与mask 1111做&操作
*所以这个最终会产生 0-15的序列.
*/
final Segment<K,V> segmentFor(int hash)
{
return segments[(hash >>> segmentShift) & segmentMask];
}
/**
*将计算的hash值补充到原始hashCode中,这是为了防止
*外部用户传进来劣质的hash值(比如重复度很高)所带来
*的危害. */
private static int hash(int h)
{
// Spread bits to regularize both segment and index locations,
// using variant of single-word Wang/Jenkins hash.
h += (h << 15) ^ 0xffffcd7d;
h ^= (h >>> 10);
h += (h << 3);
h ^= (h >>> 6);
h += (h << 2) + (h << 14);
return h ^ (h >>> 16);
}
Segment的put方法在lock()后,首先对数组长度加了新的元素之后是否会超过阈值threshold进行了判定,如果超过,那么进行rehash(),rehash()的过程相对繁琐,首先数组会自动增长一倍,然后需要对HashEntry数组中的所有元素都需要重新计算hash值,并且置到新数组的新的位置,同时为了减小操作损耗,将原来不需要移动的数据不做移动操作(power-of-two expansion,在默认threshold,在数组扩大一倍时只需要移动1/6元素,其他都可以不动)。所有动作完成之后,通过一个while循环寻找Segment中是否有相同Key存在,如果已经存在,那么根据onlyIfAbsent参数确定是否替换(如果为true,不替换,如果为false,替换掉value),然后返回替换的value,如果不存在,那么新生成一个HashEntry,并且根据一开始计算出来的index放到数组指定位置,并且累积元素计数,返回put的值。最后unlock()释放掉锁.
4. CopyOnWriteArrayList和CopyOnWriteArraySet
CopyOnWriteList<span
发表评论
-
maven实际应用
2012-05-29 15:41 1276<!--<br /> <br /&g ... -
JVM参数方法总结
2012-04-10 10:53 997这里向大家简单介绍一下如何设置JVM参数,主要包括堆设置,垃圾 ... -
linux top命令详解
2012-03-31 13:19 1007top命令和ps命令的基本作用是相同的,显示系统当前的进 ... -
eclipse热键
2012-03-19 13:24 0Ctrl+1 快速修复(最经典 ... -
HTTP 响应状态码
2012-02-21 17:22 1632HTTP 响应状态码 <script type=& ... -
JAVA 内存详解 (理解 JVM 如何使用 Windows 和 Linux 上的本机内存)
2012-02-06 15:20 1784级别: 中级 Andrew Hall , 软件工程师, IB ... -
shell字符串处理
2011-12-08 17:03 924在做shell批处理程序时 ... -
三次握手Three-way Handshake
2011-10-21 14:16 1017三次握手Three-way Handshake 一个虚拟 ... -
讨论 Setsockopt选项
2011-07-26 10:21 1206有时候我们要控制套接 ... -
TCP连接建立与关闭
2011-07-03 19:16 1223TCP 是一个面向连接的 ... -
转 Linux 终端输出字体颜色说明
2011-06-04 01:04 2790文本终端的颜色可以使用“ANSI非常规字符序列”来生成。举例: ... -
转 LVS体系结构分析
2011-05-26 12:51 1296问题 如今,无论在企 ... -
分布式服务框架 Zookeeper -- 管理分布式环境中的数
2011-05-25 14:13 1447本文介绍的 Zookeeper 是以 3.2.2 ... -
转 LVS 负载均衡算法
2011-05-23 15:13 1225轮叫调度RR(Round-Robin Schedulin ... -
转 BTrace入门及使用实例
2011-04-21 11:21 2481介绍Btrace (Byte Trace) ... -
转 GC算法
2011-04-21 10:42 1921Java与C++之间有一堵由内 ... -
使用Jstat监控gc情况
2011-04-14 10:16 1336性能测试过程中,我们该如何监控java虚拟机内存的使用情况 ... -
TCP/IP 选项TcpTimedWaitDelay设置
2011-04-13 14:34 1329TCP/IP 选项TcpTimedWaitDelay设置 ... -
HAProxy配置解释
2011-04-07 17:28 4877HAProxy配置中分成五部分内容,当然这些组件不是必 ... -
配置开发支持高并发TCP连接的Linux应用程序全攻略
2011-04-07 16:05 10081、修改用户进程可打开文件数限制 在Linux平台上,无论编 ...
相关推荐
Java并发容器是Java多线程编程中的重要工具,它们提供了高效、线程安全的数据结构,使得在并发环境下处理数据变得更加简单。在`java.util.concurrent`包中,有四种主要的并发容器类型:队列(BlockingQueue)、Map...
Java并发容器CopyOnWriteArrayList是Java并发包中提供的一个并发容器,实现了线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现。这种读写分离的并发策略使得CopyOnWriteArrayList容器允许...
《Java并发编程实践》是一本深入探讨Java多线程编程的经典著作,由Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowles和David Holmes等专家共同编写。这本书全面介绍了Java平台上的并发编程技术,是Java开发...
此外,书中还介绍了Java并发容器,如ConcurrentHashMap、CopyOnWriteArrayList和BlockingQueue等,这些都是为并发环境设计的高效数据结构。它们在多线程环境下的性能和线程安全特性使得开发者能更方便地实现并发操作...
### Java并发编程实践知识点详解 #### 一、Java并发编程基础 ##### 1.1 并发与并行概念区分 在Java并发编程实践中,首先需要理解“并发”与“并行”的区别。“并发”指的是多个任务同时进行,但实际上可能是在多...
总的来说,这份“java并发编程内部分享PPT”涵盖了Java并发编程的多个重要方面,包括线程创建与管理、同步机制、并发容器、线程池、并发问题以及异步计算。通过深入学习和实践这些知识点,开发者可以更好地应对多...
5. **并发容器**:Java并发包(`java.util.concurrent`)提供了一系列优化的并发容器,如`ConcurrentHashMap`、`CopyOnWriteArrayList`、`BlockingQueue`等,它们在内部实现了线程安全的机制,提高了并发性能。...
《JAVA并发编程艺术》是Java开发者深入理解和掌握并发编程的一本重要著作,它涵盖了Java并发领域的核心概念和技术。这本书详细阐述了如何在多线程环境下有效地编写高效、可靠的代码,对于提升Java程序员的技能水平...
《Java并发编程从入门到精通》是一本专为Java开发者设计的深度学习并发编程的书籍。作者韩剑锋,凭借其12年的IT行业经验,曾担任多家IT公司的研发总监和技术总监,以其丰富的实战经验和深厚的理论知识,为读者提供了...
1. **jcip-examples-src.jar**:这个JAR文件包含《Java并发编程实践》一书中的所有源代码实例,它是学习并发编程的重要资源。通过阅读和分析这些代码,我们可以深入理解Java并发API的用法,如`java.util.concurrent`...
《JAVA并发编程实践》是一本深入探讨Java多线程与并发控制的权威书籍,中文版本的出现使得更多国内开发者能够无障碍地学习这方面的知识。这本书不仅涵盖了理论基础,还提供了丰富的实战示例,包括源码,使读者能够...
Java并发编程是Java开发中的重要领域,涉及到多线程、线程安全以及系统性能优化等多个方面。本学习总结将深入探讨并发容器、同步容器、同步工具、死锁、异常处理、线程中断、线程池、返回结果以及同步方法等核心概念...
3. **并发集合与并发容器**:涵盖了Java并发集合框架,包括线程安全的ArrayList、LinkedList、HashMap等,并介绍了ConcurrentHashMap、CopyOnWriteArrayList等高效率的并发容器。 4. **并发工具**:讨论了Executor...
Java并发编程的核心在于理解和掌握Java内存模型(JMM)、线程同步机制、线程池以及并发容器等关键概念。首先,Java内存模型定义了线程之间的共享变量的访问规则,确保在多线程环境下数据的一致性和可见性。书中会...
《Java并发编程的艺术》是一本全面探讨Java并发编程技术的专业书籍,它深入剖析了Java并发机制的底层实现,如volatile、synchronized等关键技术的工作原理,并详细讲解了线程池、并发容器等高级并发工具的使用。...
《Java并发程序设计教程》是一本深入探讨Java平台上的并发编程技术的专业书籍。并发和多线程是现代软件开发中的核心概念,特别是在Java这样的多线程支持强大的编程语言中。这本书详细介绍了如何在Java环境中有效地...
- **并发容器**:如`ConcurrentHashMap`、`CopyOnWriteArrayList`、`BlockingQueue`等,它们内部实现了线程安全的数据结构。 4. **并发设计模式** - **生产者消费者模型**:使用`BlockingQueue`实现,生产者线程...
Java并发编程是软件开发中的重要领域,特别是在多核处理器和分布式系统中,高效地利用并发可以极大地提升程序的性能和响应速度。本资源"Java并发编程_设计原则和模式(CHM)"聚焦于Java语言在并发环境下的编程技巧、...
4. **Java并发容器** `jcip-examples-src.jar`可能包含Java并发编程实战中的各种容器示例,如`BlockingQueue`(阻塞队列)用于线程间的通信,`CopyOnWriteArrayList`和`CopyOnWriteArraySet`提供线程安全的集合操作...
Java提供了丰富的并发API,如java.util.concurrent包,包括线程池、并发容器、同步工具类等。 二、设计原则 1. **线程安全**:确保在多线程环境下,代码的执行不会因并发而产生错误或不一致的状态。这通常需要使用...