JUC(集合框架)

Java支持同步和并发多种集合框架,并发框架往往能带来更高的性能

同步类容器是 线程安全 的,如 Vector、HashTable 等容器的同步功能都是由 Collections.synchronizedMap 等工厂方法去创建实现的,底层使用 synchronized 关键字,每次只有一个线程访问容器

并发框架通常采用更好的设计来减小锁的粒度,提升吞吐;比如ConcurrentHashMap将数据分段加锁,利用读写加锁的特性代替Vector

框架对比

ConcurrentHashMap 替代 HashTable
ConcurrentSkipListMap 排序
CopyOnWriteArrayList 替代 Vector
ConcurrentLinkedQueue 高性能队列,无阻塞
LinkedBlockingQueue 阻塞形式队列,阻塞

ConcurrentMap 容器#

ConcurrentHashMap JDK7中 容器内部使用(Segment)来表示不同的部分,每个段其实就是一个小的 HashTable ,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。把一个整体分成了16个段(Segment)。也就是最高支持16个线程的并发修改操作。这也是在多线程场景时 减小锁的粒度从而降低锁竞争 一种方案。并且代码中大多共享变量使用 volatile 关键字声明,目的是第一时间获取修改的内容,性能非常好

JDK8中采用CAS 和 synchronized 来实现,并且能通过多线程协助扩容

ConcurrentMap 接口下两个重要的实现:

  • ConcurrentHashMap
  • ConcurrentSkipListMap(排序)

Copy-On-Write 容器#

CopyOnWrite 容器既写时复制的容器, 用于读多写少的场景 。往一个容器添加元素时,不直接往当前容器添加,而是先将当前容器 Copy ,复制一个新的容器,然后往新的容器添加元素,添加完成之后,再将原容器的引用指向新的容器,这样做的好处是可以对 CopyOnWrite 容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以 CopyOnWrite 容器是一种读写分离的思想,读和写不同的容器。

// Copy-On-Write 容器是一种读写分离的思想
public class CopyOnWriteArrayList<E> {
    //Copy-On-Write 容器写操作时加锁,写操作结束后解锁
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();//加锁
        try {
            //1. 获取原容器
            Object[] elements = getArray();
            int len = elements.length;
            //2. 原容器 -> Copy -> 新容器
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            //3. 往新容器写入内容
            newElements[len] = e;
            //4. 指向新容器
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();//解锁
        }
    }

    //读操作没有加锁,可以支持并发操作
    @SuppressWarnings("unchecked")
    private E get(Object[] a, int index) {
        return (E) a[index];
    }

    public E get(int index) {
        return get(getArray(), index);
    }
}

Copy-On-Write 容器下两个重要的实现:

  • CopyOnWriteArrayList
  • CopyOnWriteArraySet

ConcurrentLinkedQueue 无阻塞队列#

适合高并发场景下的队列,通过无锁的方式,实现了高并发状态下的高性能,通常 ConcurrentLinkedQueue 性能好于 LinkedBlockingQueue。它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则,且不允许 null 元素。

内部也是使用CAS的原理来实现

ConcurrentLinkedQueue 重要方法:

  1. add()和offer():添加元素(ConcurrentLinkedQueue 下两个方法无区别)。
  2. poll()和peek():取头元素节点,区别在于前者删除元素,后者不会。注意: 没有元素时返回 null,不会阻塞队列。

BlockingQueue 阻塞队列#

与 ConcurrentLinkedQueue 相比,BlockingQueue 是阻塞的,即,put 方法在队列满的时候会阻塞直到有队列成员被消费,take 方法在队列空的时候也会阻塞,直到有队列成员被放进来。

BlockingQueue API: BlockingQueue 方法以四种形式出现,对于不能立即满足但可能在将来某一时刻可以满足的操作,这四种形式的处理方式不同:

  • 第一种是抛出一个异常
  • 第二种是返回一个特殊值(null 或 false,具体取决于操作)
  • 第三种是在操作可以成功前,无限期地阻塞当前线程
  • 第四种是在放弃前只在给定的最大时间限制内阻塞。下表中总结了这些方法:
操作 抛出异常 特殊值 阻塞 超时
插入 add(e) offer(e) put(e) offer(e, time, unit)
移除 remove() poll() take() poll(time, unit)
检查 element() peek() 不可用 不可用

总结#

在并发场景中,合理的使用Java提供的并发框架能为系统性能带来质的提升。要熟练运用相关框架

posted @   糯米๓  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示