开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
本文主要讲解Java多线程情况下集合类的不安全的解决方式以及Callable的使用。
6. 集合类不安全
List不安全(ArrayList是不安全的):在多线程下会会出现并发修改异常java.util.ConcurrentModificationException。
为什么会产生这样的问题呢?多个线程调用的时候,list读取是没有问题的(固定的),但是写入的时候会有覆盖操作,那就会造成数据安全问题。
针对list产生的并发修改异常,解决方案有三种:
New Vector<>();Collections.synchronizedList(new ArrayList<>());new CopyOnWriteArrayList<>();
CopyOnWrite写入时复制, COW是计算机程序设计领域的一种优化策略。
CopyOnWriteArrayList比Vector厉害在哪? 一般只要有synchronic效率都会较低,而CopyOnWriteArrayList用的是Lock,Vector用的sync
Set不安全,也是通过上述方式解决的,但是set没有vector,所以用后面两种方式。
//解决方案一(工具类写法): Set<String> set = Collections.synchronizedSet(new HashSet<>());
//解决方案二(JUC方法): Set<String> set = new CopyOnWriteArraySet<>();
hashset的底层是什么?底层就是一个hashmap,它的add方法就是map的put一下。
public HashSet() {
map = new HashMap<>();
}
// add set 本质就是 map key是无法重复的!
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object(); // 不变得值!
Map不安全,map默认等价于new HashMap<>(16,0.75); 加载因子、初始容量。
//解决并发不安全问题new ConcurrentHashMap<>();两种方法如下:
//Map<String,String> map = new ConcurrentHashMap<>();
//Map<String,String> map= Collections.synchronizedMap(new HashMap<>());
7. Callable
jdk官方说:Callable类似于Runnable,因为他们都是为实例可能由另一个线程执行的类设计的。然而,Runnable不返回结果,也不能抛出被检查的异常。
- Callable可以有返回值,如
class MyThread implements Callable<Integer>;可以抛出异常;方法不同,call方法相当于runnable的run。
那如何用Callable呢?
1.Thread里面直接收Runnable,如何让Callable与Thread产生关联就要看Runnable了。
2.通过FutureTask,两者就产生了关联
也就是说FutureTask接收Callable,同时其上层接口又是Runnable,就可以放到Thread中了。
- 这部分代码中的MyThread是实现Callable的类
- Callable接口是有范型参数的,表示期call方法返回值就是这个范型。
- futureTask.get()获取Callable的返回结果,但这个get方法可能有参数阻塞。
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//传统的方式
// new Thread(new MyThread()).start();
// new Thread(new FutureTask<>(Callable)).start();
MyThread thread = new MyThread();
//适配类
FutureTask<Integer> futureTask = new FutureTask<>(thread);
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();//结果会被缓存,效率高
Integer o = futureTask.get(); //获取Callable的返回结果。这个get方法可能会参数阻塞
System.out.println(o);
}
}