ConcurrentHashMap的设计实现
为什么还需要ConcurrentHashMap,不是有了Hashtable吗。如果所有的事情都用Synchronized去解决,那么这个世界会变得很糟糕。
ConcurrentHashMap最绝妙的地方是采用了锁分段技术,一种分而治之的策略,一个HashMap被分为了几个Segment,在每个Segment里面实行同步控制。
对ConcurrentHashMap的操作首先找到相应的Segment,然后在Segment里面进行操作
public V put(K key, V value) { if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); return segmentFor(hash).put(key, hash, value, false); } final Segment<K,V> segmentFor(int hash) { return segments[(hash >>> segmentShift) & segmentMask]; } 这种所分段方法本身有利于多线程操作。 但是ConcurrentHashMap所做的还不止这些,源代码中对Segment中Entry的读取操作并未加锁! V get(Object key, int hash) { if (count != 0) { // read-volatile HashEntry<K,V> e = getFirst(hash); while (e != null) { if (e.hash == hash && key.equals(e.key)) { V v = e.value; if (v != null) return v; return readValueUnderLock(e); // recheck } e = e.next; } } return null; }
首先找到对应的项,如果不为null就直接返回,否则调用readValueUnderLock方法。需要注意的就是那个readValueUnderLock(e)方法
/** * Reads value field of an entry under lock. Called if value * field ever appears to be null. This is possible only if a * compiler happens to reorder a HashEntry initialization with * its table assignment, which is legal under memory model * but is not known to ever occur. */ V readValueUnderLock(HashEntry<K,V> e) { lock(); try { return e.value; } finally { unlock(); } }
源码注释的解释是编译器可能会对HashEntry进行重排序,这对JMM来说是允许的。我的理解还是此时会有其他线程会对value进行更新,所以需要加锁,等待其他线程更新之后获取value。
CopyOnWriteArrayList设计实现
所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的。
BlockingQueue设计实现
天生的生产者消费者型的数据结构。
BlockingQueue的实现,两把锁takeLock和putLock,两个信号量(条件变量)notEmpty和notFull,基于PV原理实现。Await操作支持延迟,所以相应的put和take也支持延迟操作。
ConcurrentLinkedQueue的设计实现
一个基于非阻塞算法的链表队列。非阻塞算法比较难理解的一个地方是多个指针或者引用如何运用CAS操作。此处只涉及两个指针,一offer操作为例:
public boolean offer(E e) { if (e == null) throw new NullPointerException(); Node<E> n = new Node<E>(e); retry: for (;;) { Node<E> t = tail; Node<E> p = t; for (int hops = 0; ; hops++) { Node<E> next = succ(p); if (next != null) { if (hops > HOPS && t != tail) continue retry; p = next; } else if (p.casNext(null, n)) { if (hops >= HOPS) casTail(t, n); // Failure is OK. return true; } else { p = succ(p); } } } }
对于多个指针的修改可能会使数据结构状态不一致,例如第一个引用的指向修改成功了,但是第二个修改失败的情形,这中间如果有其它线程对数据结构进行操作就有可能产生不可预知的问题。一般采用多线程协助操作的策略进行非阻塞处理,如果第一个线程有未完成的操作,后续线程能够判断出并可以帮助它继续完成。对于offer操作,如果其他线程在更新是发现尾节点一致,但是尾节点的后继节点不为空,那么表示上一线程操作未完成,则在此线程会继续在循环里设置尾节点为当前尾节点的后继。
相关推荐
《JAVA并发编程实践》是Java开发人员深入理解并发编程的一本经典著作,由Doug Lea撰写,本书中文版高清完整,包含丰富的书签,便于读者查阅和学习。这本书旨在帮助开发者掌握在Java平台上进行高效、安全并发编程的...
《Java并发编程实践》是Java开发者深入理解并发编程的重要参考资料,尤其对于想要提升多线程应用设计和性能优化技能的程序员来说,这本书提供了丰富的实践经验和深入的理论知识。以下是根据提供的章节内容概述的一些...
根据提供的信息,“Java 并发编程实战.pdf”这本书聚焦于Java并发编程的实践与应用,旨在帮助读者深入了解并掌握Java中的多线程技术及其在实际项目中的应用技巧。虽然部分内容未能提供具体章节或实例,但从标题及...
《Java并发编程1-9章》是一份涵盖了Java并发编程核心知识的资料集合,主要源自博客作者Yishizhu在iteye上的分享。通过阅读这些章节,我们可以深入了解Java平台上的多线程和并发处理机制,这对于任何需要处理高性能、...
《JAVA并发编程实践》这本书是Java开发者深入理解并发编程的重要参考资料。它涵盖了Java并发的核心概念、工具和最佳实践,旨在帮助读者在多线程环境下编写高效、安全的代码。 并发编程是现代软件开发中的关键技能,...
Java并发编程实践中的线程池是一个关键的概念,它在多线程编程中扮演着至关重要的角色,有效地管理和调度线程资源,以提高系统的性能和效率。线程池通过复用已存在的线程来减少线程的创建和销毁开销,避免了频繁的上...
"Java并发编程与实践"文档深入剖析了这一主题,旨在帮助开发者理解和掌握如何在Java环境中有效地实现并发。 并发是指在单个执行单元(如CPU)中同时执行两个或更多任务的能力。在Java中,这主要通过线程来实现,...
### Java并发编程实践 #### 一、并发编程基础 ##### 1.1 并发与并行的区别 在Java并发编程中,首先需要理解“并发”(Concurrency)和“并行”(Parallelism)的区别。“并发”指的是多个任务在同一时间段内交替...
《Java 并发编程实战》是一本专注于Java并发编程的权威指南,对于任何希望深入了解Java多线程和并发控制机制的开发者来说,都是不可或缺的参考资料。这本书深入浅出地介绍了如何在Java环境中有效地管理和控制并发...
在探讨Java并发编程与高并发解决方案的过程中,我们会涉及到一系列核心概念和相关技术。本文将基于文档《Java并发编程与高并发解决方案-学习笔记***.pdf》中提供的内容,来详细阐述并发编程和高并发的基本概念、CPU...
根据提供的文件信息,本书《JAVA并发编程实践JavaConcurrencyinPractice》由Doug Lea等著名作者撰写,是一本关于Java并发编程的专业书籍。本书主要聚焦于Java并发编程的基础理论与实践应用,适合对Java并发机制感...