跳到主要内容

06、Java并发编程 - 集合类不安全

6、集合类不安全

6.1. List不安全

list 不安全

6.1.1. 单线程:安全

package com.coding.collunsafe;

import java.util.Arrays;
import java.util.List;

public class UnsafeList1 {
   
     
    public static void main(String[] args) {
   
     
        List<String> list = Arrays.asList("a", "b", "c");
        list.forEach(System.out::println);
    }
    
}

6.1.2. 多线程:不安全

package com.coding.collunsafe;

import java.util.ArrayList;
import java.util.Random;
import java.util.UUID;

public class UnsafeList2 {
   
     
    public static void main(String[] args) {
   
     
        // 代码实现
        ArrayList<Object> list = new ArrayList<>();

        // 测试多线程下是否安全List,3条线程都不安全了
        // 多线程下记住一个异常,并发修改异常 java.util.ConcurrentModificationException
        
        // Exception  ConcurrentModificationException
        for (int i = 1; i <= 30; i++) {
   
     
            new Thread(()->{
   
     
                // 3个结果
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }

    }
}

6.1.3. 不安全怎么解决

package com.coding.collunsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 善于总结:
 * 1、 故障现象: ConcurrentModificationException
 * 2、 导致原因: 多线程操作集合类不安全
 * 3、 解决方案:
 *      List<String> list = new Vector<>(); // Vector 是一个线程安全的类,效率低下 
 *      List<String> list = Collections.synchronizedList(new ArrayList<>()); 
 *      List<String> list = new CopyOnWriteArrayList<>(); // JUC 100 推荐使用
 */
public class UnsafeList2 {
   
     
    public static void main(String[] args) {
   
     
        // 代码实现
        // ArrayList<Object> list = new ArrayList<>(); // 效率高,不支持并发!
        // List<String> list = new Vector<>(); // Vector 是一个线程安全的类,效率低下 
        // List<String> list = Collections.synchronizedList(new ArrayList<>()); 

        // 多线程高并发程序中,一致性最为重要

        // 写入时复制;  COW 思想,计算机设计领域。优化策略
        //  思想: 多个调用者,想调用相同的资源; 指针
        // 只是去读,就不会产生锁!
        // 假如你是去写,就需要拷贝一份都自己哪里,修改完毕后,在替换指针!

        List<String> list = new CopyOnWriteArrayList<>(); // JUC 

        // 测试多线程下是否安全List,3条线程都不安全了
        // 多线程下记住一个异常,并发修改异常 java.util.ConcurrentModificationException

        // Exception  ConcurrentModificationException
        for (int i = 1; i <= 30; i++) {
   
     
            new Thread(()->{
   
     
                // 3个结果
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }

    }
}

1、多线程高并发程序中,一致性最为重要

2、CopyOnWriteArrayList写入时复制; 底层原理就是给数组添加volatile关键字(有关volatile后面章节会详细讲解)
思想: 多个调用者,想调用相同的资源; 如果线程只是去读,就不会产生锁!
如果线程是去写,就需要拷贝一份到自己那里,修改完毕后,在替换原指针!原先读的线程指针就移到新的指针。

写入时复制(COW)思想原理:指针,复制指向的问题

 

6.2. Set不安全

set 不安全

package com.interview.concurrent.collection;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @author DDKK.COM 弟弟快看,程序员编程资料站
 * @description 描述:Set不安全,以及解决方案
 *  善于总结:
 *  1、 故障现象: ConcurrentModificationException 并发修改异常!
 *  2、 导致原因: 并发下 HashSet 存在安全的问题
 *  3、 解决方案:
 *      Set<String> set = Collections.synchronizedSet(new HashSet<>());  60
 *      Set<String> set =new CopyOnWriteArraySet<>();  // 100
 * @date 2023/2/22 19:56
 */
public class SetUnSafe {
   
     

    public static void main(String[] args) {
   
     
        //1、HashSet不安全测试
        //hashSet();
        //2、Set不安全解决方案一
        //collections();
        //3、Set不安全解决方案二
        copyOnWriteArraySet();
    }
    /**
     *  @description:   HashSet不支持并发
     *  1、效率高,不支持并发!
     *  2、多线程下使用HashSet,报错:线程修改异常java.util.ConcurrentModificationException
     *  @author DDKK.COM 弟弟快看,程序员编程资料站
     *  @date 2023/2/22 23:39
     */
    public static void hashSet(){
   
     
        Set<String> set = new HashSet<>(); // 底层是什么

        for (int i = 1; i <=100 ; i++) {
   
     
            new Thread(()->{
   
     
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }

    public static void collections(){
   
     

        Set<String> set = Collections.synchronizedSet(new HashSet<>());

        for (int i = 1; i <=100 ; i++) {
   
     
            new Thread(()->{
   
     
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
    public static void copyOnWriteArraySet(){
   
     

        Set<String> set =new CopyOnWriteArraySet<>();

        for (int i = 1; i <=100 ; i++) {
   
     
            new Thread(()->{
   
     
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

6.3. map 不安全

map 不安全

 

package com.interview.concurrent.collection;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author DDKK.COM 弟弟快看,程序员编程资料站
 * @description 描述:
 * 1、Map的加载因子,初始值
 * 在开发中会这样使用 HashMap 吗? 不会  一开始就知道 100大小的容量
 * 根据实际的业务设置初始值
 * 加载因子,初始值
 * Map<String, String> map = new HashMap<>(100,0.75);
 *
 * 任何存在想修改JDK源码都是不可取的
 *
 * 并发下 HashMap 不安全,ConcurrentModificationException
 * 解决方案:Map<String, String> map = new ConcurrentHashMap<>();
 * @date 2023/2/22 19:58
 */
public class MapUnSafe {
   
     

    public static void main(String[] args) {
   
     

        //1、HashMap的不安全测试
        //hashMap();
        //2、Map不安全解决方案一
        //collections();
        //3、Map不安全解决方案二
        concurrentHashMap();

    }

    /**
     *  @description:
     *  1、线程不安全,高并发下报错:java.util.ConcurrentModificationException
     *  2、key、value允许为Null
     *  @author DDKK.COM 弟弟快看,程序员编程资料站
     *  @date 2023/2/23 0:00
     */
    public static void hashMap(){
   
     
        // 人生如程序,不是选择,就是循环,学习和总结十分重要!
        Map<String, String> map = new HashMap<>();
        // 加载因子,初始值
        // Map<String, String> map = new HashMap<>(100,0.75);

        for (int i = 1; i <= 100; i++) {
   
     
            new Thread(()->{
   
     
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }

    public static void collections(){
   
     

        Map<String, String> map = Collections.synchronizedMap(new HashMap<>());

        for (int i = 1; i <= 100; i++) {
   
     
            new Thread(()->{
   
     
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
    public static void concurrentHashMap(){
   
     
        /**
         * 针对Map,没有 CopyOnWrite**类,与之对应的是ConcurrentHashMap
         */
        Map<String, String> map = new ConcurrentHashMap<>();

        for (int i = 1; i <= 100; i++) {
   
     
            new Thread(()->{
   
     
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}