跑起一个程序,并不难;难的是,能让程序跑多远!—— 一颗剽悍的种子
JUC并发系列
JUC并发系列(一):什么?听说你搞混了并发和并行
JUC并发系列(二):详解Condition实现精准通知唤醒
JUC并发系列(三):面试问并发,一问锁就懵(怒肝一篇透彻理解锁,面试不慌)
一、ArrayList非线程安全
在之前的多线程系列中有讲过ArrayList在多线程下并不安全。
深入多线程十:只有从不同案例中,才能深刻体会多线程的不安全,从而才能更好的解决
没看过的小伙伴也没关系,我们再把代码拿出来回顾一下。
1.1 ArrayList非线程安全代码示例
public class Demo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
运行结果
可以看到集合的长度添加的数量并不是我们预期的长度。
而是可能会出现覆盖。
在多线程下集合ArrayList是不安全的。
二、Vector解决ArrayList非线程安全
是不是很好奇,这里为何会提Vector,不只因为Vector是线程安全的,也因为ArrayList都来自List接口,所以可能是你学习时最常接触的,也可能是你开发中解决 ArrayList 非线程问题最常用的,当然也会是你脱口而出的面试答案。
但是,但是…
如果你只会 Vector 是远远不够的,为什么这么说,问你个小问题!
你觉得是 ArrayList 在JDK中先出现的还是 Vector ?
你的回答肯定是先 ArrayList 后再 Vector 啦,这还有问的,Vector不正是为了解决ArrayList的非线程问题的。
但是答案可能恰恰相反,在 JDK1.0 中就加入了 Vector,而ArrayList是在JDK1.2才加入的。所以 Vector是老生了,ArrayList才是那个新生(当然1.2也不算新啦)。
2.1 Vector解决ArrayList非线程代码示例
Vector解决起来很简单,只需把ArrayList关键字换成 Vector。
Vector<String> list = new Vector<String>();
Vector除了
public class Demo {
public static void main(String[] args) {
Vector<String> list = new Vector<String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
运行结果
2.2 Vector虽好,但是慎用
Vector虽然是一个线程安全的List,但是它的线程安全实现方式是对所有操作都加上了synchronized关键字,这种方式严重影响效率.所以并不推荐使用Vecto。
2.3 面试回答Vector,并不是一个加分项,只是一个答案
除了慎用,当邂逅面试时,如果仅仅答出Vector其实并不能给你加分,因为Vector算是古老的关键字了,只能算是一个答案。
但是如果你能回答Vector后,还能说出下面解决的办法,那么就可以体现你知识面的广度了。
三、 synchronizedList解决ArrayList非线程安全
synchronizedList是Collections提供的一个方法,解决起来也很简单。
List<String> list = Collections.synchronizedList(new ArrayList());
3.1 synchronizedList解决ArrayList非线程代码示例
public class Demo {
public static void main(String[] args) {
List<String> list = Collections.synchronizedList(new ArrayList());
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
运行结果
3.2 synchronizedList的优缺点
synchronizedList 的写操作性能比下面即将要介绍的 CopyOnWriteArrayList 在多线程操作时性能好很多,但读操作采用了synchronized关键字,所以读没有CopyOnWriteArrayList要好。
只要有synchronized的地方,也就是有加锁的地方,性能方面当然就没有不加锁好。
所以没有最好的方法,只有最合适的应用场景。
四、 CopyOnWriteArrayList
CopyOnWriteArrayList 主要在于 CopyOnWrite 写入时复制(简称COW),是计算机程序设计中的一种优化策略。
写入时复制其思想是在多线程下同时读取同一个资源时会共同获取指针指向的资源,但当某些线程操作(修改)同一个资源时,才会真正复制一份副本,而其他线程访问最初资源不变。
可以简单的理解是 在写入的时候避免覆盖而造成数据问题。
4.1 CopyOnWriteArrayList解决ArrayList非线程代码示例
public class Demo {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
运行结果
4.2 总结CopyOnWriteArrayList
综合上面,我想你都能总结 CopyOnWriteArrayList了,CopyOnWriteArrayList在读方面好于Vector,synchronizedList,但读写性能不如synchronizedList。
五、最后
最后的最后,为了更好的阅读体验,我把想说的话都放在了下面,嘿嘿。
我是一颗剽悍的种子 把我会的,认真的分享 是我写博客一直不变的信条。
如果你能看到这篇博文,说明咱们还是很有缘的;希望能带给你一些许帮助,创作的不易, 把我文章的知识带走,你的三连留下,点赞,评论,关注,是我最大的动力。