线程安全
一些线程安全的容器在复合操作下可能不是线程安全的
例:同步容器Vector
1.Vector这样的同步容器的所有公有方法全都是synchronized的,也就是说,我们可以在多线程场景中放心的使用【单独】这些方法,因为这些方法本身的确是线程安全的
2.由于,不同线程在同一时间操作同一个Vector,其中包括删除操作,那么就同样有可能发生线程安全问题。所以,在使用同步容器的时候,
如果涉及到多个线程同时执行删除操作,就要考虑下是否需要加锁。
3.最好使用其提供的复合的线程安全方法
线程不安全
例如:线程不安全的HashMap
在多线程环境下,使用HashMap进行put操作会引起死循环,
导致CPU利用率接近100%,所以在并发情况下不能使用HashMap。例如,执行以下代码会引起死循环。
final HashMap<String, String> map = new HashMap<> (2); Thread t = new Thread (() -> { for (int i = 0; i < 10000; i++) { new Thread (() -> map.put (UUID.randomUUID ().toString (), ""), "ftf" + i).start (); } }, "ftf"); t.start(); t.join();
1.HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的Entry链表形成环形数据结构,
一旦形成环形数据结构,Entry的next节点永远不为空,就会产生死循环获取Entry。
2.JDK1.8之前,为了提高rehash的速度,冲突链表是使用头插法,因为头插法是操作速度最快的,
找到数组位置就直接找到插入位置了,头插法在多线程下回引起死循环
3.JDK1.8之后开始加入红黑树,当链表长度大于8时链表就会转换成红黑树,这样就大大提高了在冲突链表查找的速度,同时因为链表的长度不可能大于8,
链表在rehash的消耗就小很多,所以JDK1.8使用尾插法也避免了死循环问题