首页 > 其他分享 >set集合的线程安全问题

set集合的线程安全问题

时间:2023-11-25 18:33:23浏览次数:29  
标签:set return Object current snapshot 集合 线程

一、HashSet

HashSet是基于HashMap实现的,因为HashMap本身是线程不安全的,所以HashMap就是线程不安全的,

简单看下HashSet的源码

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;
	//真正存元素的map,key是要存进set的元素,value是Object
    private transient HashMap<E,Object> map;
    
    public boolean add(E e) {
    	//添加元素时往内部的map中放入
        return map.put(e, PRESENT)==null;
    }
}

二、线程安全的set

2.1 java.util.Collections#synchronizedSet(java.util.Set)

这个工具方法接收一个set集合,对它进行包装,让每个方法都被Synchronized修饰,用这种方式保证线程安全。

因为锁对象是集合对象本身,所以存和取的方法不能同时执行,性能较低。

2.2 CopyOnWriteArraySet

这个set基于CopyOnWriteArrayList实现,保证线程安全

public class CopyOnWriteArraySet<E> extends AbstractSet<E>
        implements java.io.Serializable {
    private static final long serialVersionUID = 5457747651344034263L;
	
	//内部持有一个CopyOnWriteArrayList
    private final CopyOnWriteArrayList<E> al;
    
    //添加方法,调用的是CopyOnWriteArrayList的方法
    public boolean add(E e) {
        return al.addIfAbsent(e);
    }
}

再继续看下CopyOnWriteArrayList#addIfAbsent方法,这个方法一定是线程安全的

public boolean addIfAbsent(E e) {
    //先获取存元素的数组
    Object[] snapshot = getArray();
    //查找当前集合中是否有e
    return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
    //添加
    addIfAbsent(e, snapshot);
}

private boolean addIfAbsent(E e, Object[] snapshot) {
    final ReentrantLock lock = this.lock;
    //加锁
    lock.lock();
    try {
        //获取最新的数组
        Object[] current = getArray();
        //新数组的长度
        int len = current.length;
        //判断数组是否发生了变化
        if (snapshot != current) {
            // Optimize for lost race to another addXXX operation
            // 找出最短的长度
            int common = Math.min(snapshot.length, len);
            //这个for循环是一种针对判断效率的优化,如果在0-common中能找到e就返回false
            //因为这个snapshot在调用当前方法前已经检查过了,所以循环中current[i] = snapshot[i]的时候
            // snapshot[i]一定不等于e
            for (int i = 0; i < common; i++)
                if (current[i] != snapshot[i] && eq(e, current[i]))
                    return false;
            //查询current中的剩余元素
            if (indexOf(e, current, common, len) >= 0)
                return false;
        }
        //走到这里表示当前集合没有,需要添加元素
        Object[] newElements = Arrays.copyOf(current, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        //释放锁
        lock.unlock();
    }
}

标签:set,return,Object,current,snapshot,集合,线程
From: https://www.cnblogs.com/chengxuxiaoyuan/p/17855857.html

相关文章

  • list集合的线程安全问题分析
    一、ArrayList先说结论,ArrayList是线程不安全的。至于为什么需要去了解它的实现原理,来看下它的源码。首先ArrayList是基于数据实现的,分析它的线程安全问题需要看下add方法publicclassArrayList<E>extendsAbstractList<E>implementsList<E>,RandomAccess,Clone......
  • 多线程编程之——终止(打断)正在执行中的线程
    多线程编程之——终止(打断)正在执行中的线程ps:文字有点多,想看结果的,直接跳转:《二》一、基础知识1、我们基于spring开发,把线程都交给spring把线程交给spring管理好不好?将线程交给Spring管理是一个常见的做法,特别是在基于Spring的应用程序中。通过将线程纳入Spring的管理......
  • set中的查找操作
    P05523.ycz的setDescriptionpps就给你出了一道set入门题,他觉得你做出来了就代表你的set真正入门了。由于pps太神了,所以你根本不敢反驳,只能老老实实地做出这题。而且pps表示,如果你不能在1s之内给出答案,pps将不会保你AKIOIFormatInput第一行为n,代表操作的个数之后的n行,每行两......
  • Sumsets(UVA10125)整数集合
    备课的时候发现了这道题,对于初识哈希来说并不算一道很简单的题。在查阅林厚从老师的示例代码与往届OI选手的博客后,大致理解了本题的思路。相关标签:Hash跳转至本题Description给定一个整数集合S,求一个最大的d,满足a+b+c=d,其中a,b,c,d∈SInput多组数据,每组数据包括:第一行一......
  • winform 使用了invoke还是报错 线程间操作无效: 从不是创建控件“Form2”的线程访问它
    winform开发中,遇到“线程间操作无效:从不是创建控件“Form2”的线程访问它”,明明使用了网上说的this.invoke,怎么还是会报这个错误呢?代码如下,由于是测试configureAwait功能时发现的,所以带了它的一些使用 privateasyncvoidbutton7_Click(objectsender,EventArgse)//点......
  • 2-SET详解
    前置知识SET问题的标准定义:在计算机科学中,布尔可满足性问题(有时称为命题可满足性问题,缩写为SATISFIABILITY或SAT)是确定是否存在满足给定布尔公式的解释的问题。(全是废话)说人话就是,你要给n个变量,n需要给他赋值使它满足给你一些形如(x1为1或x3为0或x4为3)的条件,你必须满足所有条件......
  • 命令行 npm config set legacy-peer-deps true 的作用
    首先,我们需要了解npm,npm是NodePackageManager的缩写,它是Node.js的默认包管理工具。npm提供了许多命令,如install、uninstall、update等,用于管理Node.js的依赖和包。npmconfigsetlegacy-peer-depstrue是npm的一个命令,它主要用于解决npm7在处理peerdepende......
  • 一个用于多线程共享数据保护测试的简易游戏服务器代码
    #include<iostream>#include<thread>#include<list>#include<mutex>//一个线程负责从客户端读取用户的命令,放入一个队列中;//另一个线程负责从队列中读取命令并解析,假设用一个int变量代表一个命令。classA{public://这里无法模拟从网络接受命令的过程,我们......
  • delphi 遍历集合类型
    遍历集合类型代码通过for-in循环遍历usesSystem.TypInfo;procedureTForm1.Button1Click(Sender:TObject);varvAnchors:TAnchors;vAnchor:TAnchorKind;beginvAnchors:=[akLeft,akTop,akBottom];forvAnchorinvAnchorsdobeginMemo1.Lines.......
  • WPF集合绑定与数据模板
    普通List集合数据绑定publicclassDataSource{publicstringName{get;set;}="Hello";publicObservableCollection<string>list{get;set;}=newObservableCollection<string>(){......