• java 多线程 ConcurrentArrayList


    java中的List类型中,只有CopyOnWriteArrayList是线程安全的ArrayList。

    在copyOnWriteList中,基本底层还是不变:由数组构成的链表,有一个Object[]数组域。
    但是在其内部有一个ReentrantLock独占锁,在增删改的时候都是先上锁再操作。
    所以它是并发安全的。

    在实现的时候,都是先先将数组域复制到一个新数组中,然后对这个新数组进行增删改,最后将新数组赋给旧数组。

    在进行迭代iterator时,实际上是对内部数组域快照的一个迭代,如果这个数组域被修改,迭代还是按照先前的值进行迭代。
    详情见demo01.java
    package concurrent_list;/*
    name: demo01
    user: ly
    Date: 2020/5/30
    Time: 11:11
    */
     
    import java.util.Iterator;
    import java.util.concurrent.CopyOnWriteArrayList;
     
    //   CopyAndWriteArrayList的迭代弱一致性
    public class demo01 {
        private static CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<String>();
     
     
        public static void main(String []args) throws InterruptedException{
            copyOnWriteArrayList.add("a");
            copyOnWriteArrayList.add("b");
            copyOnWriteArrayList.add("c");
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    copyOnWriteArrayList.remove("c");
                    copyOnWriteArrayList.set(1,"changed");
                }
            });
     
            //在启动线程之前获取迭代器
            Iterator<String> iterable = copyOnWriteArrayList.iterator();
            thread.start();  //现在才启动
            thread.join();
            while (iterable.hasNext()){
                System.out.println(iterable.next());
            }
            System.out.println("actual:");
            iterable = copyOnWriteArrayList.iterator();
            while (iterable.hasNext()){
                System.out.println(iterable.next());
            }
     
        }
    }
    

    改进版

    很多时候,如何保障你的代码高并发高性能,这的确是个很耐人寻味的话题。高性能意味着你要对采用的每一个容器、集合(数据结构)要非常讲究,而它往往是很多人不太注意的地方,这也行就是你不能专家级的一方面原因!so,基本功很重要,尽可能的接触底层实现。在java圈中,ArrayList 是非线程安全的,难道在多线程场景下我们只有Vector这一种线程安全的数组实现可以选择么?

        当然也有List synchronizedList = Collections.synchronizedList(new ArrayList());但是当你不可避免使用contains() 进行搜索的时,它不可避免会锁住整个list,太恐怖了。

        Queue 和Deque (基于Linked List)有并发的实现是因为他们的接口相比List的接口有更多的限制,这些限制使得实现并发成为可能。

        CopyOnWriteArrayList它规避了只读操作(如get/contains)并发的瓶颈,但是它为了做到这点,在修改操作中做了很多工作和修改可见性规则。 此外,修改操作还会锁住整个List,因此这也是一个并发瓶颈。所以从理论上来说,CopyOnWriteArrayList并不算是一个通用的并发List。

    总之,数组ArrayList 虽然insert和get是最快的,但是非线程安全的,CopyOnWriteArrayList虽安全,但insert速度慢

    如何优化呢?

    其实也不复杂,只需做的这2点

        要继续发挥数组ArrayList 的优点
        要去掉锁采用高性能的CAS

    1. 高性能Unsafe

     public class UnsafeUtils {
            final static private Unsafe _unsafe;
            static {
                Unsafe tmpUnsafe = null;
         
                try {
                    java.lang.reflect.Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
                    field.setAccessible(true);
                    tmpUnsafe = (sun.misc.Unsafe) field.get(null);
                } catch (java.lang.Exception e) {
                    throw new Error(e);
                }
         
                _unsafe = tmpUnsafe;
            }
            public static final Unsafe unsafe() {
                return _unsafe;
            }
        }
        public class Unsafe_test {
            @Test
            public void test() {
                Unsafe u = UnsafeUtils.unsafe();
                int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
                //返回当前数组第一个元素地址(数组起始地址的偏移值),返回6
                int b = u.arrayBaseOffset(int[].class);
                //返回当前数组一个元素占用的字节数s,返回4
                int s = u.arrayIndexScale(int[].class);
                int pos=9;
                //获取数组对象某个位置偏移值(b + s * pos),将intval写入内存
                u.putInt(arr, (long) b + s * pos, 1);
                for (int i = 0; i < 10; i++) {
                    //获取数组对象某个位置上偏移值,从而获得元素的值
                    int v = u.getInt(arr, (long) b + s * i);
                    System.out.print(v + " ");//1 2 3 4 5 6 7 8 9 1
                }
            }
        }
    

     
    2. 保持数组ArrayList的优点

       /**
         * 仅支持get/set,不支持remove
         */
        public class ConcurrentArrayList<T> implements RandomAccess {
            public static final int MAX_CAPACITY = 1 << 30;
            private static final long SIZE_OFFSET;
            private static final int ABASE;
            private static final int ASHIFT;
            private volatile Object[] values;
            //unsafe operate
            private volatile int size;
            static {
                try {
                    Field field = ConcurrentArrayList.class.getDeclaredField("size");
                    SIZE_OFFSET = UnsafeUtils.unsafe().objectFieldOffset(field);
                    ABASE = UnsafeUtils.unsafe().arrayBaseOffset(Object[].class);
                    int scale = UnsafeUtils.unsafe().arrayIndexScale(Object[].class);
                    if ((scale & (scale - 1)) != 0) {
                        throw new Error("array index scale not a power of two");
                    }          
                    ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
                } catch (Throwable e) {
                    throw new Error(e);
                }
            }
            public T get(int index) {
                return (T) UnsafeUtils.unsafe().getObjectVolatile(values, offset(ABASE, ASHIFT, index));
            }
         
            public void add(T value) {
                int index = insertIndex();
                set(index, value);
            }
         
            private int insertIndex() {
                int index = UnsafeUtils.unsafe().getAndAddInt(this, SIZE_OFFSET, 1);
                ensureCapacity(index + 1);
                return index;
            }
         
            public void set(int index, T value) {
                final long offset = offset(ABASE, ASHIFT, index);
                for (; ; ) {// like cas
                    final Object[] before = values;
                    UnsafeUtils.unsafe().putOrderedObject(before, offset, value);
                    final Object[] after = values;
         
                    if (before == after) {
                        return;
                    }
                }
            }
         
            public ConcurrentArrayList() {
                this(16);
            }
         
            public ConcurrentArrayList(int initialCapacity) {
                if (initialCapacity > MAX_CAPACITY) {
                    throw new IndexOutOfBoundsException("Illegal initial capacity: " + initialCapacity);
                }
                ensureCapacity(initialCapacity);
            }
         
            private void ensureCapacity(int capacity) {
                Object[] theArray = values;
                if (theArray != null && theArray.length >= capacity) {
                    return;
                }
                synchronized (this) {
                    Object[] finalArray = values;
                    if (finalArray != null && finalArray.length >= capacity) {
                        return;
                    }
                    int newCapacity = tableSizeFor(capacity);
                    if (newCapacity > MAX_CAPACITY) {
                        throw new IndexOutOfBoundsException("" + newCapacity);
                    }
         
                    Object[] objs = new Object[newCapacity];
         
                    if (finalArray != null) {
                        System.arraycopy(finalArray, 0, objs, 0, finalArray.length);
                    }
                    values = objs;
                }
            }
         
            /**
             * 成倍扩容
             *
             * @param cap
             * @return
             */
            public int tableSizeFor(final int cap) {
                int n = cap - 1;
                n |= n >>> 1;
                n |= n >>> 2;
                n |= n >>> 4;
                n |= n >>> 8;
                n |= n >>> 16;
                return (n < 0) ? 1 : (n >= MAX_CAPACITY) ? MAX_CAPACITY : n + 1;
            }
            /**
             * 获取某个元素的offset
             *
             * @param arrayBase
             * @param arrayShift
             * @param index
             * @return
             */
            public long offset(final long arrayBase, final int arrayShift, final int index) {
                return ((long) index << arrayShift) + arrayBase;
            }
         
            public int size() {
                return size;
            }
            public void clear() {
                size = 0;
            }
        }
    

     
    基本的测试
        synchronizedList:157.6 ms
        arrayList:60.30 ms
        vector:164.9 ms
        concurrentArrayList:86.83 ms
        copyOnWriteArrayList:慢到无法统计

      public class ConcurrentArrayList_unit {
            private static final ArrayList<Boolean> arrayList = new ArrayList<>();
            private static final Vector<Boolean> vector = new Vector<>();
            private List synchronizedList = Collections.synchronizedList(new ArrayList());
            private static final CopyOnWriteArrayList<Boolean> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
            private static final ConcurrentArrayList<Boolean> concurrentArrayList = new ConcurrentArrayList<>();
            private int maxConcurrent = 200;
            private int maxSet = 8000;
            private int maxGet = 8000;
         
            @Test
            public void arrayList() throws InterruptedException {
                Consumer arrayListConsumer = x -> {
                    for (int i = 0; i < maxSet; i++) {
                        arrayList.add(Boolean.TRUE);
                    }
                    for (int j = 0; j < maxGet; j++) {
                        arrayList.get(j);
                    }
                };
                runTimes(maxConcurrent, "arrayList", arrayListConsumer);
            }
         
            @Test
            public void concurrentArrayList() throws InterruptedException {
                Consumer concurrentArrayListConsumer = x -> {
                    for (int i = 0; i < maxSet; i++) {
                        concurrentArrayList.add(Boolean.TRUE);
                    }
                    for (int j = 0; j < maxGet; j++) {
                        concurrentArrayList.get(j);
                    }
                };
                runTimes(maxConcurrent, "concurrentArrayList", concurrentArrayListConsumer);
            }
         
            @Test
            public void vector() throws InterruptedException {
                Consumer vectorConsumer = x -> {
                    for (int i = 0; i < maxSet; i++) {
                        vector.add(Boolean.TRUE);
                    }
                    for (int j = 0; j < maxGet; j++) {
                        vector.get(j);
                    }
                };
                runTimes(maxConcurrent, "vector", vectorConsumer);
            }
         
            @Test
            public void synchronizedList() throws InterruptedException {
                Consumer synchronizedListConsumer = x -> {
                    for (int i = 0; i < maxSet; i++) {
                        synchronizedList.add(Boolean.TRUE);
                    }
                    for (int j = 0; j < maxGet; j++) {
                        synchronizedList.get(j);
                    }
                };
                runTimes(maxConcurrent, "synchronizedList", synchronizedListConsumer);
            }
         
            @Test
            public void copyOnWriteArrayList() throws InterruptedException {
                Consumer copyOnWriteArrayListConsumer = x -> {
                    for (int i = 0; i < maxSet; i++) {
                        copyOnWriteArrayList.add(Boolean.TRUE);
                    }
                    for (int j = 0; j < maxGet; j++) {
                        copyOnWriteArrayList.get(j);
                    }
                };
                runTimes(maxConcurrent, "copyOnWriteArrayList", copyOnWriteArrayListConsumer);
            }
         
         
            private void runTimes(int maxConcurrent, String tag, Consumer consumer) throws InterruptedException {
                Stopwatch stopwatch = Stopwatch.createStarted();
                CountDownLatch latch = new CountDownLatch(maxConcurrent);
                for (int i = 0; i < maxConcurrent; i++) {
                    new Thread(() -> {
                        consumer.accept(null);
                        latch.countDown();
                    }).start();
                }
                latch.await();
                System.out.println(tag + ":" + stopwatch);
            }
        }
    



    充分利用cpu高性能位运算的,Integer.numberOfLeadingZeros()返回0位的个数,基准选定的分别是2的1、2、3、4次幂


  • 相关阅读:
    ASP.NET Page 那点事
    .Net项目分层与文件夹结构大全(最佳架子奖,吐槽奖,阴沟翻船奖揭晓)
    bash_profile和.bashrc的区别
    limits.conf生效问题
    有关snprintf返回值
    snprintf和strncpy对比
    Hadoop技术论坛
    Ubuntu系统微调
    interpreter和state模式的区别
    ANTLR实现的SQL解析器 OQL
  • 原文地址:https://www.cnblogs.com/interdrp/p/16412891.html
Copyright © 2020-2023  润新知