为什么HashMap是线程不安全的
同时put碰撞导致数据丢失
同时put扩容导致数据丢失
死循环造成cpu百分百(JDK7及之前存在)
可以参考:疫苗:JAVA HASHMAP的死循环 漫画版
putVal流程
- 判断key value不为空
- 计算hash值
- 根据对应位置节点的类型,来赋值,或者helpTransfer,或者增长链表,或者给红黑树增加节点
- 检查 满足阈值就“红黑树化
- 返回 oldVal
get流程
- 计算hash值
- 找到对应位置,根据情况进行:
- 直接取值
- 红黑树里找值
- 遍历链表取值
- 返回找到的结果
组合操作(不能又get又put)不保证ConcurrentHashMap线程安全
package predecessor;
import java.util.concurrent.ConcurrentHashMap;
public class HashMap implements Runnable{
private static ConcurrentHashMap<String,Integer> scores = new ConcurrentHashMap<>();
public static void main(String[] args) throws InterruptedException {
scores.put("小明",0);
Thread t1 = new Thread(new HashMap());
Thread t2 = new Thread(new HashMap());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(scores);
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
Integer score = scores.get("小明");
Integer newScore = score+1;
scores.put("小明",newScore);
}
}
}
保证安全,要么用synchronized (class)类锁,要么用replace(key,oldvalue,newvalue)方法
CopyOnWriteArrayList
- 代替Vector和synchronizedList
- Vector和synchronizedList的锁的粒度太大,并发效率相对比较低,并且迭代时无法编辑
- 还包括CopyOnWriteArraySet,用来替代同步Set
适用场景
- 读操作尽可能地快,而写即使慢一些也没有太大关系
- 比如:黑名单,每日更新,监听器:迭代操作远多于修改操作(读多写少)
- 不像读写锁只允许读读共享,他是读写锁规则 的升级,读取完全不用加锁,并且写入也不会阻塞读取操作。只有写入和写入之间需要同步等待
- 缺点是得到的数据有可能不是最新的
- 因为复制所以要占用双倍内存
- add方法用reentrantlock加锁,了解Reentrantlock
package predecessor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListDemo1 {
public static void main(String[] args) {
//ArrayList<String> arrayList = new ArrayList<>();
CopyOnWriteArrayList<String> arrayList = new CopyOnWriteArrayList<>();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
arrayList.add("4");
arrayList.add("5");
arrayList.add("6");
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()){
System.out.println(arrayList);
String next = iterator.next();
System.out.println(next);
if (next.equals("2")){
arrayList.remove("5");
}
}
}
}
[1, 2, 3, 4, 5, 6]
1
[1, 2, 3, 4, 5, 6]
2
[1, 2, 3, 4, 6]
3
[1, 2, 3, 4, 6]
4
[1, 2, 3, 4, 6]
5
[1, 2, 3, 4, 6]
6
注意看5这里,即使删除了,他还是找到节点5,说明,修改和迭代分开的。因为他是复制一份出来再进行的操作