zoukankan      html  css  js  c++  java
  • 5、集合类不安全

    List不安全

    单线程和多线程操作List

    单线程下使用List

    单线程下使用List都是安全的

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    public class ListTest {
        public static void main(String[] args) {
    
            List<Integer> list = Arrays.asList(1, 2, 3);
            list.forEach(System.out::println);
    
            List<String> list2 = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                list2.add(String.valueOf(i));
            }
            list2.forEach(System.out::println);
    
        }
    }

    多线程下使用List

    首先我们要知道,在高并发的环境下List容器是不安全的

    比如:下面的例子,会报错 java.util.ConcurrentModificationException 并发修改异常

    为什么会报这个异常呢?

    • 多个线程可能会操作容器中同一个存储空间,就会造成覆盖

    import java.util.ArrayList;
    import java.util.List;
    import java.util.UUID;
    
    // java.util.ConcurrentModificationException 并发修改异常
    public class ListTest {
        public static void main(String[] args) {
    
            List<String> list = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                new Thread(()->{
                    list.add(UUID.randomUUID().toString().substring(0, 5));
                    System.out.println(list);
                }, String.valueOf(i)).start();
            }
    //        System.out.println(list.size());  //main线程,可能在没有数据的情况下,先输出
        }
    }

     解决方案

    有三种:

    1、找一个替代的容器:

    new Vector<>(); 是线程安全的容器

    2、使用工具类:

    Collections.synchronizedList(new ArrayList<>()); Collection是容器的父类

    3、使用JUC提供的线程安全的容器:

    new CopyOnWriteArrayList<>(); // 拷贝一份原来的数组,在插入数据

    使用Vector容器

    import java.util.ArrayList;
    import java.util.List;
    import java.util.UUID;
    import java.util.Vector;
    
    // java.util.ConcurrentModificationException 并发修改异常
    public class ListTest {
        public static void main(String[] args) {
            /**
             * 解决方法:
             * 1、new Vector<>(); 是线程安全的容器
             */
            List<String> list = new Vector<>();
            for (int i = 0; i < 10; i++) {
                new Thread(()->{
                    list.add(UUID.randomUUID().toString().substring(0, 5));
                    System.out.println(list);
                }, String.valueOf(i)).start();
            }
    //        System.out.println(list.size());  //main线程,可能在没有数据的情况下,先输出
        }
    }

    Vector底层加了synchronized

     使用Collections工具类

    package com.zxh.unsafe;
    
    import java.util.*;
    
    // java.util.ConcurrentModificationException 并发修改异常
    public class ListTest {
        public static void main(String[] args) {
            /**
             * 解决方法:
             * 1、new Vector<>(); 是线程安全的容器
             * 2、Collection是容器的父类:使用工具类 Collections.synchronizedList(new ArrayList<>());
             */
            List<String> list = Collections.synchronizedList(new ArrayList<>());    // 创建同步容器
            for (int i = 0; i < 10; i++) {
                new Thread(()->{
                    list.add(UUID.randomUUID().toString().substring(0, 5));
                    System.out.println(list);
                }, String.valueOf(i)).start();
            }
    //        System.out.println(list.size());  //main线程,可能在没有数据的情况下,先输出
        }
    }

     JUC提供的线程安全的容器

    package com.zxh.unsafe;
    
    import java.util.*;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    // java.util.ConcurrentModificationException 并发修改异常
    public class ListTest {
        public static void main(String[] args) {
            /**
             * 解决方法:
             * 1、new Vector<>(); 是线程安全的容器 //同步方法
             * 2、Collection是容器的父类:使用工具类 Collections.synchronizedList(new ArrayList<>()); 同步方法
             * 3、使用JUC提供的线程安全的容器 new CopyOnWriteArrayList<>();  // 拷贝一份原来的数组,在插入数据
             */
            List<String> list = new CopyOnWriteArrayList<>();
            for (int i = 0; i < 10; i++) {
                new Thread(()->{
                    list.add(UUID.randomUUID().toString().substring(0, 5));
                    System.out.println(list);
                }, String.valueOf(i)).start();
            }
    //        System.out.println(list.size());  //main线程,可能在没有数据的情况下,先输出
        }
    }

    CopyOnWriteArrayList 底层实现

    进入查看源码

    • 首先查看一下容器是如何构造

     1、创建数组,调用setArray()方法

    2、放到变量array中

    3、这里涉及到了volatile关键字,这在下面会讲

     

    • 看一下是如何添加的

    1、查看add方法

    2、进入该类是实现的方法

    3、实现原理,使用了Lock锁,同步操作,将原来的数组复制一份,再添加数据。

    Set不安全

    Set容器和List一样,不同的是:

    1、无序的

    2、不能重复

    为什么是无序、不能重复呢?接下来说明。

    单线程和多线程下操作Set

    单线程下操作Set

    import java.util.HashSet;
    import java.util.Set;
    import java.util.UUID;
    
    public class SetTest {
        public static void main(String[] args) {
            Set<String> set = new HashSet<>();
            for (int i = 0; i < 10; i++) {
                set.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(set);
            }
        }
    }

     多线程下操作Set

    • 和List相同都会存在并发问题

    import java.util.HashSet;
    import java.util.Set;
    import java.util.UUID;
    
    /**
     * 同理可证:java.util.currentModifyException
     */
    public class SetTest {
        public static void main(String[] args) {
            Set<String> set = new HashSet<>();
            for (int i = 0; i < 30; i++) {
                new Thread(()->{
                    set.add(UUID.randomUUID().toString().substring(0, 5));
                    System.out.println(set);
                }, String.valueOf(i)).start();
            }
        }
    }

    解决方法

    有两种:

    因为Set没有替代的容器。

    1. 使用工具类

      • Collections.synchronizedSet(new HashSet<>());

    2. 使用JUC提供的安全容器

      • new CopyOnWriteArraySet<>();

    使用Collections工具类提供的容器

    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.UUID;
    
    /**
     * 同理可证:java.util.currentModifyException
     * 1、使用工具类 Collections.synchronizedSet(new HashSet<>());
     * 
     */
    public class SetTest {
        public static void main(String[] args) {
    //        Set<String> set = new HashSet<>();
            Set<String> set = Collections.synchronizedSet(new HashSet<>());
    
            for (int i = 0; i < 30; i++) {
                new Thread(()->{
                    set.add(UUID.randomUUID().toString().substring(0, 5));
                    System.out.println(set);
                }, String.valueOf(i)).start();
            }
        }
    }

    使用JUC提供的CopyOnWriteArraySet

    import java.util.Set;
    import java.util.UUID;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    /**
     * 同理可证:java.util.currentModifyException
     * 1、使用工具类 Collections.synchronizedSet(new HashSet<>());
     * 2、使用JUC提供的:new CopyOnWriteArraySet<>();
     */
    public class SetTest {
        public static void main(String[] args) {
    //        Set<String> set = new HashSet<>();
    //        Set<String> set = Collections.synchronizedSet(new HashSet<>());
            Set<String> set = new CopyOnWriteArraySet<>();
    
            for (int i = 0; i < 30; i++) {
                new Thread(()->{
                    set.add(UUID.randomUUID().toString().substring(0, 5));
                    System.out.println(set);
                }, String.valueOf(i)).start();
            }
        }
    }

    HashSet的底层

    底层描述

    很简单就是,就是new了一个HashMap,将HashMap的键用于存储数据;

    所以Set容器的特征:就是无序、不可重复的容器

    查看源码

    第一步查看构造

    1、查看源码,进入HashSet类

    2、可以很清楚的看到 new 了一个 HashMap

    第二步查看add方法

    1、进入add方法

    2、找到HashSet重写的add方法

    3、看到没有,就是将元素put到map容器的键中

    4、map中value的值就是一个常量,不会改变的

    Map不安全

    HashMap底层原理暂时待学习

    单线程和多线程下操作Map

    单线程下操作Map

    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    
    public class MapTest {
        public static void main(String[] args) {
            Map<String, Object> map = new HashMap<>();
            for (int i = 0; i < 10; i++) {
                map.put(String.valueOf(i), UUID.randomUUID().toString().substring(0, 5));
            }
            map.entrySet().forEach(System.out::println);
        }
    }

    多线程下操作Map

    • 会存在并发问题

    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    
    public class MapTest {
        public static void main(String[] args) {
            Map<String, Object> map = new HashMap<>();
            for (int i = 0; i < 10; i++) {
                new Thread(()->{
                    map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                    System.out.println(map);
                }, String.valueOf(i)).start();
            }
        }
    }

    解决方法

    有两种:

    因为Set没有替代的容器。

    1. 使用工具类

      • Collections.synchronizedMap(new HashMap<>());

    2. 使用JUC提供的安全容器

      • new ConcurrentHashMap<>()

    使用Collections工具类提供的容器

    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    
    public class MapTest {
        public static void main(String[] args) {
    //        Map<String, Object> map = new HashMap<>();
            Map<String, Object> map = Collections.synchronizedMap(new HashMap<>());
    
            for (int i = 0; i < 30; i++) {
                new Thread(()->{
                    map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                    System.out.println(map);
                }, String.valueOf(i)).start();
            }
        }
    }

    使用JUC提供的ConcurrentHashMap

    import java.util.Map;
    import java.util.UUID;
    import java.util.concurrent.ConcurrentHashMap;
    
    public class MapTest {
        public static void main(String[] args) {
    //        Map<String, Object> map = new HashMap<>();
    //        Map<String, Object> map = Collections.synchronizedMap(new HashMap<>());
            Map<String, Object> map = new ConcurrentHashMap<>();
    
            for (int i = 0; i < 30; i++) {
                new Thread(()->{
                    map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                    System.out.println(map);
                }, String.valueOf(i)).start();
            }
        }
    }
    致力于记录学习过程中的笔记,希望大家有所帮助(*^▽^*)!
  • 相关阅读:
    探讨.net Socket支持在线连接数量
    Net Configuration Agent
    Http压力测试工具HttpTest4Net
    TCP连接有效性检测方法
    SocketAsyncEventArgs使用解说
    可靠、高吞吐架构基础改造
    PerformanceCounter蛋痛的设计
    谱聚类(spectral clustering)原理总结
    用scikit-learn学习DBSCAN聚类
    DBSCAN密度聚类算法
  • 原文地址:https://www.cnblogs.com/zxhbk/p/12955133.html
Copyright © 2011-2022 走看看