温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

java中并发容器J.U.C怎么用

发布时间:2021-10-19 15:52:31 来源:亿速云 阅读:124 作者:柒染 栏目:大数据

这篇文章将为大家详细讲解有关java中并发容器J.U.C怎么用,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

并发容器是JDK提供的一个包名:java.util.concurrent

ArrayList -> CopyOnWriteArrayList

CopyOnWriteArrayList是线程安全的,写操作时复制,当有新元素添加到CopyOnWriteArrayList时先从原有的list中拷贝出来,然后在新的list上写操作,写完之后将原来的list指向新的list,整个操作都是在锁的保护下进行的,这样做为了防止多线程下多个add操作时产生多个副本,导致最终的数据不是我们期望的。

CopyOnWriteArrayList有几个缺点:

  1. 由于写操作时需要拷贝数组,因此比较消耗内存。当元素内容比较多时会导致Full GC

  2. 不能用于实时读的场景,拷贝数组需要时间,所以调用一个set操作后,读取到的数据还可能是旧的,虽然能做到最终一致性,但是无法满足实时性要求。因此CopyOnWriteArrayList更适合读多写少的场景。如果不清楚add或者set多少次操作,这个CopyOnWriteArrayList最好慎用。

HashSet、TreeSet->CopyOnWriteArraySet、ConcurrentSkipListSet

CopyOnWriteArraySet同样也是线程安全的,底层实现是CopyOnWriteArrayList,因此CopyOnWriteArraySet适合大小比较小的set集合只读操作大于写操作,因为需要复制基础数组,所以对于可变的操作(add set)的开销大。使用迭代器的迭代速度很快,而且不会有线程安全问题。

ConcurrentSkipListSet与TreeSet用一样,是支持自然排序的,可以在构造时自定义比较器。在多线程情况下ConcurrentSkipListSet里面的contains()、add()、remove()是线程安全的,多个线程可以并发的执行插入移除和访问操作,但是对于批量操作例如addAll(),removeAll(),retainAll()、containsAll()并不能保证以原子方式执行,这些操作可以被其他线程打断,需要额外增加锁才行,因为他们实现方式是分别调用contains()、add()、remove()的。因为并发容器只能保证每一次的contains()、add()、remove()操作时原子性的,而不能保证每一次批量操作都不会被其他线程打断。也就是多个add,多个remove操作时有其他线程进来。

HashMap、TreeMap -> ConcurrentHashMap、ConcurrentSkipListMap

ConcurrentHashMap不允许null,在实际的应用中除了少数的插入操作和删除操作外,绝大部分我们使用map都是使用读取操作,而且读操作大多数都是成功的,基于这个前提,ConcurrentHashMap针对读操作做了大量的优化,因此这个类具有很高的并发性,高并发场景下有很好的表现。

ConcurrentSkipListMap是TreeMap的线程安全版本。内部是使用skipList跳表的结构实现的。ConcurrentHashMap的存取速度是ConcurrentSkipListMap的4倍左右,但是ConcurrentSkipListMap的key是有序的而ConcurrentHashMap是做不到的,ConcurrentSkipListMap支持更高的并发,ConcurrentSkipListMap的存取时间是与线程数无关的,在数据量一定的情况下并发线程数越多ConcurrentSkipListMap越能体现出优势。

在较低并发情况下,可以使用Collections.synchronizedSortedMap()来实现,也可以提供较好的效率。在高并发的情况下可以使用ConcurrentSkipListMap提供更高的并发度。要对键值对进行排序时可以使用ConcurrentSkipListMap。

@Slf4j
@ThreadSafe
public class ConcurrentHashMapExample {
    // 请求总数
    public static int clientTotal = 5000;

    // 同时并发执行的线程数
    public static int threadTotal = 200;

    private static Map<Integer, Integer> map = new ConcurrentHashMap<>();

    public static void main(String[] args) throws InterruptedException {
        //线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //定义信号量
        final Semaphore semaphore = new Semaphore(threadTotal);
        //定义计数器
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for(int i = 0; i < clientTotal; i++) {
            final int count  = i;
            executorService.execute(() ->{
                try {
                    semaphore.acquire();
                    update(count);
                    semaphore.release();
                } catch (InterruptedException e) {

                    log.error("exception", e);
                }
                countDownLatch.countDown();

            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("size:{}",map.size()) ;
    }

    public static void update(int i) {
        map.put(i,i);
    }

}

输出结果正确。

 concurrentSkipListMap:

private static Map<Integer, Integer> map = new ConcurrentSkipListMap<>();

J.U.C

java中并发容器J.U.C怎么用

安全共享对象策略 - 总结

  • 线程限制:一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改。

  • 共享只读:一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问,但是任何线程都不能修改它。

  • 线程安全对象:一个线程安全的对象或者容器,在内部通过同步机制来保证线程安全,所以其他线程无需额外的同步就可以通过公共接口随意访问它。

  • 被守护对象:被守护对象只能通过获取特定的锁来访问。

关于java中并发容器J.U.C怎么用就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI