java多线程详解 六_Java多线程详解(六)------常见集合的线程不安全实例(NoSafeDemo)...

一、ArrayList线程不安全

如下代码演示:

1 packagecom.study.nosafedemo;2

3 import java.util.*;4

5 public classNoSafeDemo {6 public static voidmain(String[] args) {7 List list = new ArrayList<>();8 for (int i = 0; i <= 30; i++) {9 new Thread(() ->{10 list.add(UUID.randomUUID().toString().substring(0, 8));11 System.out.println(list);12 }, String.valueOf(i)).start();13 }14 }15 }16

17

18 java.util.ConcurrentModificationException

java.util.ConcurrentModificationException

ArrayList在迭代的时候如果同时对其进行修改就会

抛出java.util.ConcurrentModificationException异常

并发修改异常

看ArrayList的源码

public boolean add(E e) {

ensureCapacityInternal(size + 1); // Increments modCount!!

elementData[size++] = e;

return true;

}

没有synchronized线程不安全

二、解决方案:

1、List list = new Vector<>();

看Vector的源码

public synchronized boolean add(E e) {

modCount++;

ensureCapacityHelper(elementCount + 1);

elementData[elementCount++] = e;

return true;

}

有synchronized线程安全

2、List list = Collections.synchronizedList(new ArrayList<>());

Collections提供了方法synchronizedList保证list是同步线程安全的

那HashMap,HashSet是线程安全的吗?也不是

所以有同样的线程安全方法

32736dca1ab8993cf008cf0bdf8a5519.png

3、 写时复制

f0cab3aa8b28aaf4a8b3680c9683ca07.png

List list = new CopyOnWriteArrayList<>();

三、写时复制

不加锁性能提升出错误,加锁数据一致性但性能下降,怎么解决?

A thread-safe variant of ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array.

CopyOnWriteArrayList是arraylist的一种线程安全变体,

其中所有可变操作(add、set等)都是通过生成底层数组的新副本来实现的。

d49a1166a7603f9434cc222fba9d2ebd.png

1

2 /**

3 * Appends the specified element to the end of this list.4 *5 *@parame element to be appended to this list6 *@return{@codetrue} (as specified by {@linkCollection#add})7 */

8 public booleanadd(E e) {9 final ReentrantLock lock = this.lock;10 lock.lock();11 try{12 Object[] elements =getArray();13 int len =elements.length;14 Object[] newElements = Arrays.copyOf(elements, len + 1);15 newElements[len] =e;16 setArray(newElements);17 return true;18 } finally{19 lock.unlock();20 }21 }22

23

24

25 CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,26 而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后向新的容器Object[] newElements里添加元素。27 添加元素后,再将原容器的引用指向新的容器setArray(newElements)。28 这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。29 所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。30

31

扩展1:

Set set = new HashSet<>();//线程不安全

Set set = new CopyOnWriteArraySet<>();//线程安全

HashSet底层数据结构是什么?

HashMap ?

但HashSet的add是放一个值,而HashMap是放K、V键值对

public HashSet() {

map = new HashMap<>();

}

private static final Object PRESENT = new Object();

public boolean add(E e) {

return map.put(e, PRESENT)==null;

}

扩展2:

Map map = new HashMap<>();//线程不安全

Map map = new ConcurrentHashMap<>();//线程安全

代码:

1 packagecom.atguigu.gmall.jucdemo;2

3 import java.util.*;4 importjava.util.concurrent.ConcurrentHashMap;5 importjava.util.concurrent.CopyOnWriteArrayList;6 importjava.util.concurrent.CopyOnWriteArraySet;7

8 /**

9 * 请举例说明集合类是不安全的10 */

11 public classNotSafeDemo {12 public static voidmain(String[] args) {13

14 Map map = new ConcurrentHashMap<>();15 for (int i = 0; i <30 ; i++) {16 new Thread(()->{17 map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));18 System.out.println(map);19 },String.valueOf(i)).start();20 }21

22

23 }24

25 private static voidsetNoSafe() {26 Set set = new CopyOnWriteArraySet<>();27 for (int i = 0; i <30 ; i++) {28 new Thread(()->{29 set.add(UUID.randomUUID().toString().substring(0,8));30 System.out.println(set);31 },String.valueOf(i)).start();32 }33 }34

35 private static voidlistNoSafe() {36 //List list = Arrays.asList("a","b","c");37 //list.forEach(System.out::println);38 //写时复制

39 List list = new CopyOnWriteArrayList<>();40 //new CopyOnWriteArrayList<>();41 //Collections.synchronizedList(new ArrayList<>());42 //new Vector<>();//new ArrayList<>();

43

44 for (int i = 0; i <30 ; i++) {45 new Thread(()->{46 list.add(UUID.randomUUID().toString().substring(0,8));47 System.out.println(list);48 },String.valueOf(i)).start();49 }50 }51

52

53 }54

55

56

57

58

59 /**

60 * 写时复制61 CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,62 而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后向新的容器Object[] newElements里添加元素。63 添加元素后,再将原容器的引用指向新的容器setArray(newElements)。64 这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。65 所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。66

67 *68 *69 *70 *71

72 public boolean add(E e) {73 final ReentrantLock lock = this.lock;74 lock.lock();75 try {76 Object[] elements = getArray();77 int len = elements.length;78 Object[] newElements = Arrays.copyOf(elements, len + 1);79 newElements[len] = e;80 setArray(newElements);81 return true;82 } finally {83 lock.unlock();84 }85 }86 */

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值