JAVA数据结构篇--13线程安全的Set 集合

前言:java 中用于存放不重复元素的set 集合,其中无序的HashSet,以及有序的LinkedHashSet和TreeSet 都是非线程安全的,那么多线程环境下,我们要存放不重复的元素,需要使用哪种集合进行数据存取;

1 使用:

 Set<User> set = Collections.synchronizedSet(new LinkedHashSet<>(10, 0.75f));
 CopyOnWriteArraySet<User> copyOnWriteArraySet = new CopyOnWriteArraySet<>();
 Set<User> setFromMap = Collections.newSetFromMap(new ConcurrentHashMap<>());

 User userOne = new User();
 userOne.setId(1).setName("lisi").setAge(20);
 set.add(userOne);
 copyOnWriteArraySet.add(userOne);
 setFromMap.add(userOne);
 User userTwo = new User();
 userTwo.setId(2).setName("wangwu").setAge(20);
 set.add(userTwo);
 copyOnWriteArraySet.add(userTwo);
 setFromMap.add(userTwo);
 set.remove(userTwo);

 Iterator iterator1 = set.iterator();
 while (iterator1.hasNext()) {
     User user = (User) iterator1.next();
     System.out.println("user = " + user);
 }

2 过程:
2.1 放入获取元素:
Collections.synchronizedSet:通过使用synchronized 关键字修饰达到线程安全的目的

public Iterator<E> iterator() {
     return c.iterator(); // Must be manually synched by user!
 }

 public boolean add(E e) {
     synchronized (mutex) {return c.add(e);}
 }
 public boolean remove(Object o) {
     synchronized (mutex) {return c.remove(o);}
 }

CopyOnWriteArraySet<>():通过CopyOnWriteArrayList实现,也即底层数据结构使用的数组

private final CopyOnWriteArrayList<E> al;

 /**
  * Creates an empty set.
  */
 public CopyOnWriteArraySet() {
     al = new CopyOnWriteArrayList<E>();
 }
public boolean add(E e) {
	// 当元素不存在的时候添加元素
    return al.addIfAbsent(e);
}
publicboolean addIfAbsent(E e) {
   Object[] snapshot = getArray();
   return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
       addIfAbsent(e, snapshot);
}

/**
* A version of addIfAbsent using the strong hint that given
* recent snapshot does not contain e.
*/
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
           // 从现有数组中判断要存入的e元素是否已经存在,存在直接返回false
           int common = Math.min(snapshot.length, len);
           for (int i = 0; i < common; i++)
               if (current[i] != snapshot[i] && eq(e, current[i]))
                   return false;
           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();
   }
}
// 移除元素
public boolean remove(Object o) {
    Object[] snapshot = getArray();
    // 元素存在则进行移除否则直接返回
    int index = indexOf(o, snapshot, 0, snapshot.length);
    return (index < 0) ? false : remove(o, snapshot, index);
}
 private boolean remove(Object o, Object[] snapshot, int index) {
    final ReentrantLock lock = this.lock;
    lock.lock();// 获取锁
    try {
        Object[] current = getArray();
        int len = current.length;
        //  a: { break a; } 语法将a:之后的代码成为一个方法体,遇到 break跳出方法体
        if (snapshot != current) findIndex: {// 如果数组长度已经发生变化
            int prefix = Math.min(index, len);
            for (int i = 0; i < prefix; i++) {
                if (current[i] != snapshot[i] && eq(o, current[i])) {
                    index = i;
                    break findIndex;
                }
            }
            if (index >= len)
                return false;
            if (current[index] == o)
                break findIndex;
            index = indexOf(o, current, index, len);
            if (index < 0)
                return false;
        }
        // 数组长度-1
        Object[] newElements = new Object[len - 1];
        // 赋值剩下的元素到新的数组中
        System.arraycopy(current, 0, newElements, 0, index);
        System.arraycopy(current, index + 1,
                         newElements, index,
                         len - index - 1);
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}
// 元素遍历:
public Iterator<E> iterator() {
    return al.iterator();
}
// CopyOnWriteArrayList 下iterator
public Iterator<E> iterator() {
    return new COWIterator<E>(getArray(), 0);
}
private COWIterator(Object[] elements, int initialCursor) {
    cursor = initialCursor;
    snapshot = elements;
}
public boolean hasNext() {
    return cursor < snapshot.length;
}
public boolean hasPrevious() {
    return cursor > 0;
}
@SuppressWarnings("unchecked")
public E next() {
    if (! hasNext())
        throw new NoSuchElementException();
    return (E) snapshot[cursor++];
}

Collections.newSetFromMap(new ConcurrentHashMap<>()):使用ConcurrentHashMap实现元素的存取:

public static <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
    return new SetFromMap<>(map);
}
private final Map<E, Boolean> m;  // The backing map
private transient Set<E> s;       // Its keySet

SetFromMap(Map<E, Boolean> map) {
    if (!map.isEmpty())
        throw new IllegalArgumentException("Map is non-empty");
    m = map;
    s = map.keySet();
}
public boolean remove(Object o)   { return m.remove(o) != null; }
public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }
public Iterator<E> iterator()     { return s.iterator(); }

3 总结:
3.1 Collections.synchronizedSet() 工具类通过对方法增加synchronized 关键字修饰达到线程安全的目的;CopyOnWriteArraySet 通过ReentrantLock 获取和释放锁达到线程安全的目的;Collections.newSetFromMap(new ConcurrentHashMap<>()) 借用ConcurrentHashMap 线程安全的Map集合达到线程安全的目的;
3.2 Collections.synchronizedSet() 工具类直接使用synchronized,并发情况下性能较差;CopyOnWriteArraySet 借助CopyOnWriteArrayList 使用ReentrantLock 性能好一些,但是底层使用了数组,占用内存较多;Collections.newSetFromMap(new ConcurrentHashMap<>()) 通过ConcurrentHashMap性能较好;

你可能感兴趣的:(java基础篇,java工具篇,java,数据结构,开发语言)