- 1.ConcurrentHashMap
- 2.ConcurrentHashMap和HashTable的区别
- 3.ConcurrentHashMap线程安全的具体实现方式/底层具体实现
- 4.说说CopyOnWriteArrayList
1.ConcurrentHashMap
java5.0在juc包中提供了大量支持并发的容器类,采用“锁分段”机制,Concurrentlevel分段级别,默认16,就是有16个段(segment),每个段默认又有16个哈希表(table),每个又有链表连着。
好处:每个segment都是独立的锁,有多个线程可以并行,1.8之后,去掉了分段锁,改成了cas,效率高,不阻塞不涉及上下文切换,ConcurrentHashMap同步容器类是Java5增加的一个线程安全的哈希表,对与多线程的操作,介于HashMap与HashTable之间,内部,进而提高性能。
2. ConcurrentHashMap 和 Hashtable 的区别
底层数据结构: JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用数组+链表/红黑二叉树(当某个solt的元素个数超过8个并且table的容量>=64的时候,链表转红黑树)。Hashtable 是采用 数组+链表 的形式。
实现线程安全的方式(重要): ① 在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争。
到了 JDK1.8 直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;
② Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。
3. ConcurrentHashMap线程安全的具体实现方式/底层具体实现
1.7首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。
ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成。
Segment 实现了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。
static class Segment<K,V> extends ReentrantLock implements Serializable {
}
一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment的锁。
JDK1.8ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。Java 8在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为O(N))转换为红黑树(寻址时间复杂度为O(log(N)))
synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。
4. 说说CopyOnWriteArrayList
CopyOnWriteArrayList(写入并复制)也是juc里面的,它解决了并发修改异常,每当有写入的时候,就在底层重新复制一个新容器写入,最后把新容器的引用地址赋给旧的容器,在别人写入的时候,其他线程读数据,依然是旧容器的线程。这样是开销很大的,所以不适合频繁写入的操作。适合并发迭代操作多的场景。
虚假唤醒问题。
线程通信Condition其实就是lock中实现synchronized的wait和nofity工作。
synchronized最多仅仅能锁住当前JVM的线程。对于其他server的线程无能为力。
ReadWriteLock读写锁
不是说一个线程来了就独占,读写锁是写写互斥,读写互斥,读读共享。它维护了两个锁,读锁可以让多个线程并发持有。readwritelock写了例子。