JUC中的Set安全类集合

前言

set集合在java中本质上是无序的,关于为什么会无序,在底层源码中有很详细的说明。
在这里插入图片描述
本质上依旧还是一个 HashMap 集合,其中保存数据的方式为保存至key中。
在这里插入图片描述
关于更加详细的为什么是无序,可以参考另外一篇博客内容(java源码—hashmap源码分析(jdk1.8)),这里不做过多的阐述。

为什么多线程下的普通set集合不安全

如标题所述,为什么在多线程执行条件下,set集合存在不安全的问题,来看下列代码案例:

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

public class SetTest {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        // 不安全
        Set<String> sets = new HashSet();
        for (int i = 1; i <= 40; i++) {
    
    
            new Thread(()->{
    
    
                sets.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(Thread.currentThread().getName()+"=="+sets);
            },String.valueOf(i)).start();
        }
    }
}

在这里插入图片描述
在多线程操作同一个set集合对象时,增加数据依旧会出现 java.util.ConcurrentModificationException 异常信息。

Collections下的安全集合

和Collections集合工具类中,针对List创建安全集合的方式一样,也提供了一种创建安全set集合的方式。

import java.util.*;
public class SetTest {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        // 不安全
        //Set<String> sets = new HashSet();
        Set<String> sets = Collections.synchronizedSet(new HashSet<>());
        for (int i = 1; i <= 40; i++) {
    
    
            new Thread(()->{
    
    
                sets.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(Thread.currentThread().getName()+"=="+sets);
            },String.valueOf(i)).start();
        }
    }
}

其中的源码和list集合类似:
在这里插入图片描述
在这里插入图片描述
也是在add操作执行的方法中添加了synchronized 同步代码块,保证多个线程下,操作set集合的安全性。

JUC下的安全Set集合

同样在 jdk 1.5后,juc针对set集合也提供有安全set集合类。如下所示:

import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
public class SetTest {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        // 不安全
        //Set<String> sets = new HashSet();
        //Set<String> sets = Collections.synchronizedSet(new HashSet<>());
        Set<String> sets = new CopyOnWriteArraySet<>();
        for (int i = 1; i <= 40; i++) {
    
    
            new Thread(()->{
    
    
                sets.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(Thread.currentThread().getName()+"=="+sets);
            },String.valueOf(i)).start();
        }
    }
}

其源码逻辑如下所示:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

CopyOnWriteArrayList中的方式一样,采取java.util.concurrent.locks.ReentrantLock实现加锁和释放锁等操作。

1、调用add方法后,拿到java.util.concurrent.locks.ReentrantLock对象信息。
2、调用 lock.lock() 拿到锁!
3、将原数组对象copy操作,并创建原数组大小+1的新数组。
4、将新数据放入新数组中。
5、任何操作finally,都进行锁的释放

猜你喜欢

转载自blog.csdn.net/qq_38322527/article/details/114756909