一、JUC
java.util.concurrent下的类就叫JUC类,JUC下典型的类有:
1.ReentrantLock可重入锁
2.Semaphore信号量
3.CountDownLatch计数器
4.CyclicBarrier循环屏障
二、线程安全&非安全容器
2.1非线性安全容器
2.2线性安全容器
三、关于HashMap
hashMap是不安全的,体现如下:
- 在jdk1.7中,多线程环境下,扩容时因为采用头插法会造成环形链或数据丢失。
- 在jdk1,8中,多线程环境下,会发生数据覆盖问题
3.1hash JDK1.7死循环
jdk1.7中hashMap是由数组+链表组成
jdk1.8中hashMap是由数组+红黑树组成
链表升级为红黑树的要求:
当链表长度>8并且数组长度>64时,才会升级为红黑树。
正常情况下的扩容:
旧hashMap的节点会依次转移到新hashMap中,旧hashMap转移的顺序是A ,B ,C而新hashMap使用的是头插法,所以最终在新hashMap中的顺序是C, B, A如下图所示:
死循环是因为并发hashMap扩容导致的,并发扩容第一步,线程T1和线程T2要对hashMap进行扩容操作,此时T1和T2指向的是链表的头节点元素A,而T1和T2的下一个节点也就是T1.next和T2.next指向的是B节点,如下图:
死循环的第二部操作是,线程T2时间片用完进入休眠状态,而线程T1开始执行扩容操作,一直到线程T1扩容完成后,线程T2才被唤醒,扩容之后的场景如下图:
从上图可知线程T1执行之后,因为是头插法,所以HashMap的顺序已经发生了变化,但线程T2对于发生的一切是不可知的,所以它的指向元素依然没变,T2指向的是A元素,T2.next指向的节点是B元素。
3.2线程安全的字典
HashMap本身是不安全的,在多线程环境下可以使用哈希表可以使用Hashtable 和 ConcurrentHashMap
3.2.1Hashtable
Hashtable只是简单的把关键方法加上了 synchronized 关键字,这相当于直接对Hashtable对象本身加锁,但这样效率很低。
3.2.2ConcurrentHashMap
ConcurrentHashMap在不同的JDK中实现是不一样的。
在 JDK 1.7 中它使用的是数组加链表的形式实现的,而数组⼜分为:大数组 Segment 和小数组 HashEntry
ConcurrentHashMap 的线程安全是建立在 Segment 加锁的基础上的,所以我们把它称之为分段锁或片段锁,如下图:
在 JDK 1.7 中,ConcurrentHashMap 虽然是线程安全的,但因为它的底层实现是数组 + 链表的形式,所以在数据比较多的情况下访问是很慢的,因为要遍历整个链表,⽽ JDK 1.8 则使用了数组 + 链表/红黑树的方式优化了 ConcurrentHashMap 的实现。
在 JDK 1.8 中我们可以简单的认为,ConcurrentHashMap 是在头节点加锁来保证线程安全的,锁的粒度相比 Segment 来说更小了,发生冲突和加锁的频率降低了,并发操作的性能就提高了,具体如下图: