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次幂


posted @ 2022-06-26 06:50  锐洋智能  阅读(560)  评论(0编辑  收藏  举报