集合

Concurrent 集合

我们已经通过 ReentrantLock 和 Condition 实现了一个 BlockingQueue

public class TaskQueue {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private Queue<String> queue = new LinkedList<>();

    public void addTask(String s) {
        lock.lock();
        try {
            queue.add(s);
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public String getTask() {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                condition.await();
            }
            return queue.remove();
        } finally {
            lock.unlock();
        }
    }
}

BlockingQueue 的意思就是说,当一个线程调用这个 TaskQueue 的 getTask() 方法时,该方法内部可能会让线程变成等待状态,直到队列条件满足不为空,线程被唤醒后,getTask() 方法才会返回。
因为 BlockingQueue 非常有用,所以我们不必自己编写,可以直接使用 Java 标准库的 java.util.concurrent 包提供的线程安全的集合:ArrayBlockingQueue 。
除了 BlockingQueue 外,针对 List 、Map 、Set 、Deque 等,java.util.concurrent 包也提供了对应的并发集合类。我们归纳一下:
image
使用这些并发集合与使用非线程安全的集合类完全相同。我们以 ConcurrentHashMap 为例

Map<String, String> map = new ConcurrentHashMap<>();
// 在不同的线程读写:
map.put("A", "1");
map.put("B", "2");
map.get("A", "1");

因为所有的同步和加锁的逻辑都在集合内部实现,对外部调用者来说,只需要正常按接口引用,其他代码和原来的非线程安全代码完全一样。即当我们需要多线程访问时,把:

Map<String, String> map = new HashMap<>();

改为

Map<String, String> map = new ConcurrentHashMap<>();

就可以了。

java.util.Collections 工具类还提供了一个旧的线程安全集合转换器,可以这么用

Map unsafeMap = new HashMap();
Map threadSafeMap = Collections.synchronizedMap(unsafeMap);

但是它实际上是用一个包装类包装了非线程安全的 Map ,然后对所有读写方法都用 synchronized 加锁,这样获得的线程安全集合的性能比 java.util.concurrent 集合要低很多,所以不推荐使用。

posted @ 2023-07-07 23:13  HopeLive  阅读(7)  评论(0)    收藏  举报