zoukankan      html  css  js  c++  java
  • Java多线程详解(六)------常见集合的线程不安全实例(NoSafeDemo)

    一、ArrayList线程不安全

     如下代码演示:

     1 package com.study.nosafedemo;
     2 
     3 import java.util.*;
     4 
     5 public class NoSafeDemo {
     6     public static void main(String[] args) {
     7         List<String> 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<String> list = new Vector<>();

    看Vector的源码
    public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
    }
    有synchronized线程安全

    2、List<String> list = Collections.synchronizedList(new ArrayList<>());
    Collections提供了方法synchronizedList保证list是同步线程安全的

    那HashMap,HashSet是线程安全的吗?也不是
    所以有同样的线程安全方法

    3、 写时复制

     List<String> 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等)都是通过生成底层数组的新副本来实现的。

     1  
     2 /**
     3  * Appends the specified element to the end of this list.
     4  *
     5  * @param e element to be appended to this list
     6  * @return {@code true} (as specified by {@link Collection#add})
     7  */
     8 public boolean add(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<String> set = new HashSet<>();//线程不安全

    Set<String> 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<String,String> map = new HashMap<>();//线程不安全

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

    代码:

     1 package com.atguigu.gmall.jucdemo;
     2 
     3 import java.util.*;
     4 import java.util.concurrent.ConcurrentHashMap;
     5 import java.util.concurrent.CopyOnWriteArrayList;
     6 import java.util.concurrent.CopyOnWriteArraySet;
     7 
     8 /**
     9  * 请举例说明集合类是不安全的
    10  */
    11 public class NotSafeDemo {
    12     public static void main(String[] args) {
    13 
    14         Map<String,String> 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 void setNoSafe() {
    26         Set<String> 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 void listNoSafe() {
    36         //        List<String> list = Arrays.asList("a","b","c");
    37         //        list.forEach(System.out::println);
    38         //写时复制
    39         List<String> 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      */
  • 相关阅读:
    Asp.net 中 OnClientClick 与 OnClick 的区别
    WPF + RDLC + Tablix动态生成列 + 表头合并
    统计表用于查询效果太差的情况
    TeamView WaitforConnectFailed错误原因
    瞄到BindingGroup用法
    最讨厌工作时候百度的信息
    Lambda获取类属性的名字
    附加属性作用于多选控件,用于获取所有选中项
    文件夹图标错落解决方案
    WPF Line 的颜色过度动画
  • 原文地址:https://www.cnblogs.com/zsy-code/p/13781623.html
Copyright © 2011-2022 走看看