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();
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/zxhbk/p/12955133.html