多线程基础(八)并发数据结构并发容器

1. 并发List
Vector和CopyOnWriteArrayList,是两个并发list的实现
CopyOnWriteArrayList:实现原理是利用了对象的不变性,当没有进行写操作时,不加锁,当有写操作时先复制一份对象副本,然后进行修改,最后保存时加锁,通过以上方式减少锁竞争,从原理来看比较适合读多写少场景。
Vector:读和写都试用了synchorinized关键字,高并发读写下性能较差。但是写多读少场景有时比CopyOnWriteArrayList性能更好
我们先来测试一下读多写少场景

public class ConcurrentTest {
    //线程池线程数量
    private static Integer threadNum = 50;
    //并发任务量
    private static Integer taskNum = 200;
    //数据量
    private static Integer dataNum = 200;

    static CopyOnWriteArrayList<Integer> concurrentList = new CopyOnWriteArrayList<>();
    //private static Vector<Integer> concurrentList = new Vector<>();

    static {
        for (int i = 0; i < dataNum; i++) {
            concurrentList.add(i);
        }
    }

    static class MyArray extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < dataNum; i++) {
                concurrentList.get(i);
            }
        }
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        ThreadPoolExecutor poolExecutor = new CounterThreadPool(threadNum, threadNum, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        CounterThreadPool.startTime = start;
        for (int i = 0; i < taskNum; i++) {
            poolExecutor.submit(new MyArray());
        }
        poolExecutor.shutdown();
    }

    static class CounterThreadPool extends ThreadPoolExecutor {
        public static long startTime = 0;
        private AtomicInteger size = new AtomicInteger(0);

        public CounterThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        }

        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            int i = size.incrementAndGet();
            if (i == taskNum) {
                long end = System.currentTimeMillis();
                System.out.println("耗时:====:" + (end - startTime));
            }
        }
    }
}

切换容器,运行两次结果
CopyOnWriteArrayList:7ms
Vector:15ms
可以看到高并发读场景,CopyOnWriteArrayList性能更好,性能提高一倍。
修改get方法改为add,测试一下写性能:
CopyOnWriteArrayList:967ms
Vector:14ms
高并发写场景,Vector性能更好,性能高一个数量级。

2. 并发Set
和List一样,并发set也有一个CopyOnWriteArraySet容器,它的内部完全依赖CopyOnWriteArrayList,因此性能和CopyOnWriteArrayList类似 是和并发读
并发写场景可以使用Collections.synchronizedSet(new HashSet<>());获得一个同步容器
经过实际测试,并发写性能CopyOnWriteArraySet非常差相差两个数量级,建议使用synchronizedSet
由于Set接口没有get方法,经实际测试,contains方法两者差别并不大。
测试代码和上面的类似大家自己试一下就好了,这里就不贴了

3. 并发Map
由于Map使用率非常高Doug Lea大神专门为大家写了一个并发Map容器----ConcurrentHashMap
ConcurrentHashMap<>(),无论是并发读性能还是并发写性能,都比synchronizedMap高一倍。所以并发读写都推荐大家使用ConcurrentHashMap。另外还有一个古老的HashTable由于会锁整张表性能较差所以一般不用。
测试代码和上面的类似大家自己试一下就好了,这里就不贴了
关于其原理大家可以看这一篇文章:Java并发编程笔记之ConcurrentHashMap原理探究
4. 并发Queue
在并发队列上JDK提供了两套实现:ConcurrentLinkedQueue为代表的高性能队列。一个是以BlockingQueue为代表的阻塞队列,
单纯从高性能读写上来说ConcurrentLinkedQueue性能要高于BlockingQueue,但是BlockingQueue的设计主要功能并不是高性能,而是在于高性能的同时简化多线程间的数据共享问题,所以线程池的参数用的是BlockingQueue而不是ConcurrentLinkedQueue。
此外还有,Disruptor是一个开源的Java框架,它被设计用于在生产者—消费者(producer-consumer problem,简称PCP)问题上获得尽量高的吞吐量(TPS)和尽量低的延迟。Disruptor是LMAX在线交易平台的关键组成部分,LMAX平台使用该框架对订单处理速度能达到600万TPS,除金融领域之外,其他一般的应用中都可以用到Disruptor,它可以带来显著的性能提升。其实Disruptor与其说是一个框架,不如说是一种设计思路,这个设计思路对于存在“并发、缓冲区、生产者—消费者模型、事务处理”这些元素的程序来说,Disruptor提出了一种大幅提升性能(TPS)的方案。
Disruptor比BlockingQueue性能更好,storm框架使用的就是Disruptor
5. 并发Deque
LinkedBlockingDeque是一种双端队列(Double-Ended Queue),它的内部维护着一个前驱节点一个后驱节点,没有进行读写锁的分离,性能较差,主要是体统了一些双端队列功能。

发布了52 篇原创文章 · 获赞 7 · 访问量 3814

猜你喜欢

转载自blog.csdn.net/maomaoqiukqq/article/details/99707507