多线程下的安全集合

135 阅读1分钟

这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战

安全集合

对于需要进行多线程操作的集合来说,使用普通的单线程的集合存储数据,就可能会出现线程安全问题,下面来介绍常见的多线程集合.

list

当多线程向list中插入值时,会报错。因为ArrayList是线程不安全的

ArrayList< String > strings = new ArrayList<>();
​
        // 创建10个线程同时插入list
        for (int i = 0; i < 100 ; i++) {
            new Thread(()->{
                strings.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(strings);
            },String.valueOf(i)).start();
        }

image.png

解决方法

1.使用vector。vector是线程安全,可以解决这个问题。

在源码中,add增加了syn关键字

public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

2.使用Collections 方法类的转换成synchronizedList方法。可以将ArrayList转换成线程安全的list

给该list加上了一mutex的Object锁

List<String> strings = Collections.synchronizedList(new ArrayList<>());
​
​
public static <T> List<T> synchronizedList(List<T> list) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }

3.使用CopyOnWriteArrayList<>() ;

写入时复制.是一种优化策略. 多线程读取时,固定.写入时,先复制在写入再返回,避免覆盖

源码中使用lock锁。 所以

public void add(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        if (index > len || index < 0)
            throw new IndexOutOfBoundsException("Index: "+index+
                                                ", Size: "+len);
        Object[] newElements;
        int numMoved = len - index;
        if (numMoved == 0)
            // 写的时候copyOf
            newElements = Arrays.copyOf(elements, len + 1);
        else {
            newElements = new Object[len + 1];
            System.arraycopy(elements, 0, newElements, 0, index);
            System.arraycopy(elements, index, newElements, index + 1,
                             numMoved);
        }
        newElements[index] = element;
        setArray(newElements);
    } finally {
        lock.unlock();
    }
}

set

set和list同理

1.Collections.Collections.synchronizedSet

2.new CopyOnWriteArraySet()

hashSet就是HashMap,但是添加的时候key不能相同

public HashSet() {
        map = new HashMap<>();
    }
​
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

map

hashMap:创建时默认加载因子0.75,初始容量16

public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
​
static final float DEFAULT_LOAD_FACTOR = 0.75f;
​
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// 最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;

1.hashTable ,使用syn关键字 ,所以效率低。不允许key和value为null

public synchronized V put(K key, V value) {

1.Collections.synchronizedMap

2.new ConcurrentHashMap<>()

// 初始容量
private static final int DEFAULT_CAPACITY = 16;
​
// 默认并发数量
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
​

concurrentHashMap

jdk1.7中,底层通过lock+segment+hashEntry 实现

该map由多个segment组成,segment集成lock