2022.8.21 集合类不安全

6、集合类不安全

1、并发下ArrayList 不安全

 1  package com.xing.Unsafe;
 2  3  import java.util.*;
 4  import java.util.concurrent.CopyOnWriteArrayList;
 5  6  public class ListTest {
 7      public static void main(String[] args) {
 8          //并发下ArrayList是不安全的   会报错
 9 10          // List<String> list = new ArrayList<>();
11          // for (int i =0; i< 10; i++){
12          //     new Thread(()->{
13          //         list.add(UUID.randomUUID().toString().substring(0,5));
14          //         System.out.println(list);
15          //     },String.valueOf(i)).start();
16          // }
17 18          /**
19           * 解决方案:
20           * 1.List<String> list = new Vector<>();
21           *
22           *集合的顶级:Collections
23           * 2.List<String> list = Collections.synchronizedList(Arrays.asList("1", "2", "3"));
24           *
25           * 3.List<String> list = new CopyOnWriteArrayList<>();
26           */
27          /** CopyOnWriteArrayList 写入时复制    COW:计算机程序设计领域的一种优化策略;
28           *  多个线程调用时,list读取的时候是固定的,写入(存在覆盖)
29           *  在写入的时候避免覆盖,造成数据问题
30 31           *  CopyOnWriteArrayList 比 Vector 好在哪里?  Vector使用了synchronized 效率低
32           *  写入的时候复制一份
33           */
34 35          List<String> list = new CopyOnWriteArrayList<>();
36          for (int i =0; i< 10; i++){
37              new Thread(()->{
38                  list.add(UUID.randomUUID().toString().substring(0,5));
39                  System.out.println(list);
40              },String.valueOf(i)).start();
41          }
42      }
43  }
44

 

总结:多个线程调用的时候,List读取的时候是固定的,写入时可能会被覆盖,那么就要先CopyOnWrite写入时复制一份出来调用,调用写完再放回去,再写入的时候避免覆盖,造成数据问题。

CopyOnWriteArrayList 比 Vector牛在哪里

Vector 的 add方法源码:

1  // Vector 的 add方法
2      public synchronized boolean add(E e) {
3          modCount++;
4          ensureCapacityHelper(elementCount + 1);
5          elementData[elementCount++] = e;
6          return true;
7      }

Vector用了sychronized同步方法(同步方法自动加锁解锁)效率会很多

CopyOnWriteArrayList 的add方法源码:

 1  //CopyOnWriteArrayList 的add方法
 2  3       public boolean add(E e) {
 4          final ReentrantLock lock = this.lock;
 5          lock.lock();
 6          try {
 7              Object[] elements = getArray();
 8              int len = elements.length;//取出长度
 9              Object[] newElements = Arrays.copyOf(elements, len + 1);//拿出复制
10              newElements[len] = e;//并且修改
11              setArray(newElements);//让后将数据set回去
12              return true;
13          } finally {
14              lock.unlock();
15          }
16      }

 

总结:CopyOnWriteArrayList用的是写入时候复制之后再set回去效率而高出很多

2、并发下Set不安全

 1  package com.xing.Unsafe;
 2  3  import java.util.Set;
 4  import java.util.UUID;
 5  import java.util.concurrent.CopyOnWriteArraySet;
 6  7  public class SetTest {
 8      public static void main(String[] args) {
 9          //ConcurrentModificationException 并发修改异常
10          /*
11          有问题:与List一样报错   Set<String> strings = new HashSet<>();
12          解决方案:Set<String> strings1 = Collections.synchronizedSet(new HashSet<String>());
13          最佳解决方案:Set<String> strings = new CopyOnWriteArraySet<>();
14          */
15          Set<String> strings = new CopyOnWriteArraySet<>();
16          for (int i = 0; i < 30; i++) {
17              new Thread(()->{
18                  strings.add(UUID.randomUUID().toString().substring(0, 5));
19                  System.out.println(strings);
20              },String.valueOf(i)).start();
21          }
22      }
23  }
24

 

HashSet源码

 1   //HashSet源码:
 2  3      public HashSet() {
 4          map = new HashMap<>();
 5      }
 6  7  8    //HashSet本质就是 map key是无法重复的
 9    public boolean add(E e) {
10          return map.put(e, PRESENT)==null;
11      }
12 13 14  private static final Object PRESENT = new Object();//不变的值

 

3、HashMap不安全

 1  package com.xing.Unsafe;
 2  3  import java.util.Map;
 4  import java.util.UUID;
 5  import java.util.concurrent.ConcurrentHashMap;
 6  7  public class MapTest {
 8      public static void main(String[] args) {
 9          // HashMap<String, String> hashMap = new HashMap<>();
10          //map是这样用的么? 不是,工作中不用HashMap
11          
12          
13          //默认等价于什么? new HashMap<>(16,0.75)   第一个为初始化容量,第二个参数为默认的加载因子
14          //Map<String,String> map = new HashMap<>();//线程不安全
15          
16          
17          Map<String,String> map = new ConcurrentHashMap<>();//安全
18          
19          for (int i = 0; i<=30; i++){
20              new Thread(()->{
21                  map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
22                  System.out.println(map);
23              },String.valueOf(i)).start();
24          }
25 26      }
27  }
28

 

7、Callable

 

 

 

  • 可以有返回值

  • 可以抛出异常

  • 方法不同 run() / call()

Thread():参数只能是Runnable,怎么与Callable挂上勾?通过Runnable

 

 

 

 

 

 

 

 

 

FutureTask是个适配类,这里可以看到可以通过FutureTask可以两边互相勾搭使用,Runnavle又和Thread有关系

 1  package com.xing.Unsafe;
 2  3  import java.util.concurrent.Callable;
 4  import java.util.concurrent.ExecutionException;
 5  import java.util.concurrent.FutureTask;
 6  7  public class CallableTest {
 8      public static void main(String[] args) throws ExecutionException, InterruptedException {
 9          // new Thread(new Runnable()).start();
10          //Runnable接口的实现类为FutureTask
11          //new Thread(new FutureTask<>(Callable)).strat();
12 13 14          MyThread myThread = new MyThread();
15 16          //适配类,使Callable可以被调用
17          FutureTask futureTask = new FutureTask<>(myThread);
18 19          //两个线程执行会打印几个call
20          //一个,结果会被缓存,提高效率,因此只打印一个call
21          new Thread(futureTask,"A").start();
22          new Thread(futureTask,"B").start();
23 24          // 获取Callable的返回结果 
25          // get方法可能会产生阻塞,把它放到最后,或者使用异步通信
26          Integer integer = (Integer)futureTask.get();
27          System.out.println(integer);
28      }
29  }
30 31  /*
32  class MyThread implements Runnable{
33      @Override
34      public void run() {
35 36      }
37  }*/
38 39  //<Integer> 代表所需要的返回值
40  class MyThread implements Callable<Integer>{
41      @Override
42      public Integer call() throws Exception {
43          System.out.println("call()");
44          return 1024;
45      }
46  }
47

 

posted @ 2022-08-21 21:30  暴躁C语言  阅读(52)  评论(0编辑  收藏  举报