并发编程之:数据结构

1.并发List

并发list有三种使用模式

第一种使用Collections.synchronizedList方法进行包装:

List arrayList = new ArrayList();
//使用Collections.synchronizedList方法进行包装
List list = Collections.synchronizedList(arrayList);

我们进入该包装方法内部查看源码发现,实际上是有新创建了一个SynchronizedList对象,而SynchronizedList类对接对原有ArrayList类对象进行了包装,并对原对象的操作加上了同步控制。源码如下:

static class SynchronizedList<E>
    extends SynchronizedCollection<E>
    implements List<E> {
    private static final long serialVersionUID = -7754090372962971524L;

    final List<E> list;

    SynchronizedList(List<E> list) {
        super(list);
        this.list = list;
    }
    SynchronizedList(List<E> list, Object mutex) {
        super(list, mutex);
        this.list = list;
    }

    public boolean equals(Object o) {
        if (this == o)
            return true;
        synchronized (mutex) {return list.equals(o);}
    }
    public int hashCode() {
        synchronized (mutex) {return list.hashCode();}
    }

    public E get(int index) {
        synchronized (mutex) {return list.get(index);}
    }
    public E set(int index, E element) {
        synchronized (mutex) {return list.set(index, element);}
    }
    public void add(int index, E element) {
        synchronized (mutex) {list.add(index, element);}
    }
    public E remove(int index) {
        synchronized (mutex) {return list.remove(index);}
    }

    public int indexOf(Object o) {
        synchronized (mutex) {return list.indexOf(o);}
    }
    public int lastIndexOf(Object o) {
        synchronized (mutex) {return list.lastIndexOf(o);}
    }

    public boolean addAll(int index, Collection<? extends E> c) {
        synchronized (mutex) {return list.addAll(index, c);}
    }

    public ListIterator<E> listIterator() {
        return list.listIterator(); // Must be manually synched by user
    }

    public ListIterator<E> listIterator(int index) {
        return list.listIterator(index); // Must be manually synched by user
    }

    public List<E> subList(int fromIndex, int toIndex) {
        synchronized (mutex) {
            return new SynchronizedList<>(list.subList(fromIndex, toIndex),
                                        mutex);
        }
    }

    @Override
    public void replaceAll(UnaryOperator<E> operator) {
        synchronized (mutex) {list.replaceAll(operator);}
    }
    @Override
    public void sort(Comparator<? super E> c) {
        synchronized (mutex) {list.sort(c);}
    }

    /**
     * SynchronizedRandomAccessList instances are serialized as
     * SynchronizedList instances to allow them to be deserialized
     * in pre-1.4 JREs (which do not have SynchronizedRandomAccessList).
     * This method inverts the transformation.  As a beneficial
     * side-effect, it also grafts the RandomAccess marker onto
     * SynchronizedList instances that were serialized in pre-1.4 JREs.
     *
     * Note: Unfortunately, SynchronizedRandomAccessList instances
     * serialized in 1.4.1 and deserialized in 1.4 will become
     * SynchronizedList instances, as this method was missing in 1.4.
     */
    private Object readResolve() {
        return (list instanceof RandomAccess
                ? new SynchronizedRandomAccessList<>(list)
                : this);
    }
}

第二种使用List的同步实现类Vector:

我们通过查看Vector源码发现,Vector类实现了List接口,并实现的接口方法都是同步的方式实现,由于Vector中的实现方法是同步方法必然会对元素存储效率产生严重影响。如get/set方法代码片段

/**
 * Returns the element at the specified position in this Vector.
 *
 * @param index index of the element to return
 * @return object at the specified index
 * @throws ArrayIndexOutOfBoundsException if the index is out of range
 *            ({@code index < 0 || index >= size()})
 * @since 1.2
 */
public synchronized E get(int index) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    return elementData(index);
}

/**
 * Replaces the element at the specified position in this Vector with the
 * specified element.
 *
 * @param index index of the element to replace
 * @param element element to be stored at the specified position
 * @return the element previously at the specified position
 * @throws ArrayIndexOutOfBoundsException if the index is out of range
 *         ({@code index < 0 || index >= size()})
 * @since 1.2
 */
public synchronized E set(int index, E element) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

/**
 * Appends the specified element to the end of this Vector.
 *
 * @param e element to be appended to this Vector
 * @return {@code true} (as specified by {@link Collection#add})
 * @since 1.2
 */
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

第三种使用List实现类CopyOnWriteArrayList:

CopyOnWriteArrayList的内部实现与Vector不同,当对象进行读操作时,直接返回结果,操作中不进行同步控制;当进行写操作时会在该方法上面加上锁,然后复制一个副本列表,对副本进行修改,最后将副本写回原来列表。

其get/set/add方法源码片段如下

/**
 * {@inheritDoc}
 *
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E get(int index) {
    return get(getArray(), index);
}

/**
 * Replaces the element at the specified position in this list with the
 * specified element.
 *
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E set(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        E oldValue = get(elements, index);

        if (oldValue != element) {
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len);
            newElements[index] = element;
            setArray(newElements);
        } else {
            // Not quite a no-op; ensures volatile write semantics
            setArray(elements);
        }
        return oldValue;
    } finally {
        lock.unlock();
    }
}

/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return {@code true} (as specified by {@link Collection#add})
 */
public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

总是所知CopyOnWriteArrayList只是对修改进行了同步控制,所以在读操作上面比上面两种方式要快一些。

2.并发set

第一种方式也是有Collections.synchronizedSet方法进行包装:

Set hashSet = new HashSet();
//Collections.synchronizedSet进行包装
Set set = Collections.synchronizedSet(hashSet);

包装之后新生产一个包装了原对象的SynchronizedSet对象,SynchronizedSet对象的读写操作还是对原有的set进行读写,只是在包装的读写方法上面加上了同步控制。

第二种和list比较相似,并发set也有一个CopyOnWriteArraySet实现:

它实现了set接口,并且是线程安全的。它的内部实现完全依赖于CopyOnWriteArrayList我们可以查看源码

public class CopyOnWriteArraySet<E> extends AbstractSet<E>
        implements java.io.Serializable {
    private static final long serialVersionUID = 5457747651344034263L;

    private final CopyOnWriteArrayList<E> al;

    /**
     * Creates an empty set.
     */
    public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }

    /**
     * Creates a set containing all of the elements of the specified
     * collection.
     *
     * @param c the collection of elements to initially contain
     * @throws NullPointerException if the specified collection is null
     */
    public CopyOnWriteArraySet(Collection<? extends E> c) {
        if (c.getClass() == CopyOnWriteArraySet.class) {
            @SuppressWarnings("unchecked") CopyOnWriteArraySet<E> cc =
                (CopyOnWriteArraySet<E>)c;
            al = new CopyOnWriteArrayList<E>(cc.al);
        }
        else {
            al = new CopyOnWriteArrayList<E>();
            al.addAllAbsent(c);
        }
    }

    /**
     * Returns the number of elements in this set.
     *
     * @return the number of elements in this set
     */
    public int size() {
        return al.size();
    }

    /**
     * Returns {@code true} if this set contains no elements.
     *
     * @return {@code true} if this set contains no elements
     */
    public boolean isEmpty() {
        return al.isEmpty();
    }

    public boolean contains(Object o) {
        return al.contains(o);
    }

    /**
     */
    public Object[] toArray() {
        return al.toArray();
    }

    /**
     */
    public <T> T[] toArray(T[] a) {
        return al.toArray(a);
    }

    /**
     */
    public void clear() {
        al.clear();
    }

    /**
     */
    public boolean remove(Object o) {
        return al.remove(o);
    }

    /**
     */
    public boolean add(E e) {
        return al.addIfAbsent(e);
    }

    /**
     */
    public boolean containsAll(Collection<?> c) {
        return al.containsAll(c);
    }

    /**
     */
    public boolean addAll(Collection<? extends E> c) {
        return al.addAllAbsent(c) > 0;
    }

    /**
     */
    public boolean removeAll(Collection<?> c) {
        return al.removeAll(c);
    }

    /**
     */
    public boolean retainAll(Collection<?> c) {
        return al.retainAll(c);
    }

    /**
     */
    public Iterator<E> iterator() {
        return al.iterator();
    }

    /**
     */
    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof Set))
            return false;
        Set<?> set = (Set<?>)(o);
        Iterator<?> it = set.iterator();

        // Uses O(n^2) algorithm that is only appropriate
        // for small sets, which CopyOnWriteArraySets should be.

        //  Use a single snapshot of underlying array
        Object[] elements = al.getArray();
        int len = elements.length;
        // Mark matched elements to avoid re-checking
        boolean[] matched = new boolean[len];
        int k = 0;
        outer: while (it.hasNext()) {
            if (++k > len)
                return false;
            Object x = it.next();
            for (int i = 0; i < len; ++i) {
                if (!matched[i] && eq(x, elements[i])) {
                    matched[i] = true;
                    continue outer;
                }
            }
            return false;
        }
        return k == len;
    }

    public boolean removeIf(Predicate<? super E> filter) {
        return al.removeIf(filter);
    }

    public void forEach(Consumer<? super E> action) {
        al.forEach(action);
    }

    /**
     */
    public Spliterator<E> spliterator() {
        return Spliterators.spliterator
            (al.getArray(), Spliterator.IMMUTABLE | Spliterator.DISTINCT);
    }

    /**
     */
    private static boolean eq(Object o1, Object o2) {
        return (o1 == null) ? o2 == null : o1.equals(o2);
    }
}

3.并发Map

同样适用Map时也可以通过Collections.synchronizedMap进行包装实现一个线程安全的Map,但是这不是最优的实现方式,由于Map是一个我们适用相当频繁的数据结构,所以JDK为提供了一个专用于高并发的Map实现ConcurrentHashMap,它的内部实现了锁分离,为多线程并发下高性能提供了保证。

ConcurrentHashMap是专门为线程并发设计的Map,它的get方法是无锁的,它的put方法操作的锁力度又小于同步的map所以整体性能高于同步的。

4.并发Queue

在并发队列上,JDK提供了两套实现,一个是以ConcurrentListQueue为代表的高性能队列,一个是以BlockingQueue为代表的阻塞队列。他们都继承自Queue接口。

ConcurrentListQueue:

是一个适合于高并发场景下的队列。它通过无锁的方式,实现了高并发状态下的高性能。

BlockingQueue:

是阻塞队列BlockingQueue的代表实现,主要应用场景是生产者消费者的共享数据。由于LinkedBlockingQueue提供一种阻塞等待的机制,比如当消费者速度快于生产者时队列中的消息很快就会被清空,此时消费者再从队列中取数据时就会被阻塞等待。同理当生产者速度快于消费者时,队列很快就会被放满数据此时再往队列里面放数据时生产者就会被阻塞等待。

BlockingQueue队列主要实现方法:

offer(object):将object添加到BlockingQueue里,如果队列有足够空间就返回true,否则返回false(该方法不会阻塞当前执行的线程)。

offer(object,timeout,timeunit):是阻塞队列特有的方法,将object添加到BlockingQueue里,如果队列有足够空间则立即返回true,如果队列没有足够空间则堵塞timeout时间段,如果还添加不进去返回fase。

put(object):把object添加到队里,如果队列已满则该线程会阻塞直到队列有空间加进去。

poll():从队列里面取出元素,如果没有返回null,该方法不会阻塞线程。

poll(timeout,timeunit):从队列里面取出元素。如果队里里面没有元素会阻塞timeout时间段,如果超时还没有数据可取则返回null。

take():从队列里面取出元素,如果队列里面没有元素则线程会阻塞直到有新元素加入队列。

drainTo(list):将队列里面的数据一次性全部取出到list里面,该方法可以一次性取出所有数据到list中不用进行堵塞,可以提高数据提取效率。

ArrayBlockingQueue:

是一种基于数组的阻塞队列实现,内部维护了一个定长数组用于换成数据对象。另外还保存着两个整形变量分别标记着队列的头部和尾部在数组中的位置。

LinkedBlockingQueue:

是一种基于链表的阻塞队列实现。

5.并发Deque(Double-Ended Queue)双端队列

deque是一个可以在队列的头部或尾部进行入队和出队操作,新增操作如下:

有三个实现类:LinkedList、ArrayDeque、LinkedBlockingDeque。

LinkedList使用链表实现的双端队列,ArrayDeque使用数组实现的双端队列。通常情况下由于ArrayDeque基于数组实现,具有高效的随机访问性能,因此ArrayDeque具有更好的遍历性能。但是当队列大小变化较大时ArrayDeque需要重新分配内存,进行数组复制,在这种情况下基于链表的LinkedList具有更好的表现。但是无论LinkedList或是ArrayDeque都不是线程安全的。

LinkedBlockingDeque是一个线程安全的双端队列实现。LinkedBlockingDeque是一个链表结构,每一个队列节点都维护一个前驱节点和一个后驱节点。LinkedBlockingDeque没有进行读写锁分离,因此在高并发使用中其性能要低于LinkedBlockingQueue,更低于ConCurrentLinkedQueue。

转载于:https://my.oschina.net/u/3100849/blog/865076

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值