同步容器与并发容器

Posted _mcj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了同步容器与并发容器相关的知识,希望对你有一定的参考价值。

1.同步容器

1.1 什么是同步容器

同步容器是指那些在容器内部已经同步化了,使我们在并发操作使用容器的时候不需要进行手动同步了。

1.2 同步容器的分类

同步容器可以分为两大类:普通类和内部类
普通类
主要是Vector、Stack、HashTable
普通类其实现的方式是通过在方法上添加synchronized关键字来进行实现的。就比如,Vector容器的add,set,get方法,其源码如下:

    public synchronized E get(int index) 
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        return elementData(index);
    
    
    public synchronized E set(int index, E element) 
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    
    
    public synchronized boolean add(E e) 
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    

从上面vector的源码可以看出其主要是通过synchronized关键字的方式实现的。
内部类
是指Collections创建的内部类,比如Collections.SynchronizedList、 Collections.SynchronizedSet等
内部类是把原有的容器进行包装(通过this.list = list直接指向需要同步的容器),然后局部加锁,这样一来,就生成了线程安全的类;源码如下:

static class SynchronizedCollection<E> implements Collection<E>, Serializable 
        private static final long serialVersionUID = 3053995032091335093L;

        final Collection<E> c;  // Backing Collection
        final Object mutex;     // Object on which to synchronize

        SynchronizedCollection(Collection<E> c) 
            this.c = Objects.requireNonNull(c);
            mutex = this;
        

        SynchronizedCollection(Collection<E> c, Object mutex) 
            this.c = Objects.requireNonNull(c);
            this.mutex = Objects.requireNonNull(mutex);
        

        public int size() 
            synchronized (mutex) return c.size();
        
        public boolean isEmpty() 
            synchronized (mutex) return c.isEmpty();
        
        public boolean contains(Object o) 
            synchronized (mutex) return c.contains(o);
        
        public Object[] toArray() 
            synchronized (mutex) return c.toArray();
        
        public <T> T[] toArray(T[] a) 
            synchronized (mutex) return c.toArray(a);
        

        public Iterator<E> iterator() 
            return c.iterator(); // Must be manually synched by user!
        

        public boolean add(E e) 
            synchronized (mutex) return c.add(e);
        
        public boolean remove(Object o) 
            synchronized (mutex) return c.remove(o);
        

        public boolean containsAll(Collection<?> coll) 
            synchronized (mutex) return c.containsAll(coll);
        
        public boolean addAll(Collection<? extends E> coll) 
            synchronized (mutex) return c.addAll(coll);
        
        public boolean removeAll(Collection<?> coll) 
            synchronized (mutex) return c.removeAll(coll);
        
        public boolean retainAll(Collection<?> coll) 
            synchronized (mutex) return c.retainAll(coll);
        
        public void clear() 
            synchronized (mutex) c.clear();
        
        public String toString() 
            synchronized (mutex) return c.toString();
        
        // Override default methods in Collection
        @Override
        public void forEach(Consumer<? super E> consumer) 
            synchronized (mutex) c.forEach(consumer);
        
        @Override
        public boolean removeIf(Predicate<? super E> filter) 
            synchronized (mutex) return c.removeIf(filter);
        
        @Override
        public Spliterator<E> spliterator() 
            return c.spliterator(); // Must be manually synched by user!
        
        @Override
        public Stream<E> stream() 
            return c.stream(); // Must be manually synched by user!
        
        @Override
        public Stream<E> parallelStream() 
            return c.parallelStream(); // Must be manually synched by user!
        
        private void writeObject(ObjectOutputStream s) throws IOException 
            synchronized (mutex) s.defaultWriteObject();
        
    

可以看到他是对其对西昂mutex进行加锁,来保证方法的同步。
同步类和内部类的区别

区别 普通类 内部类
锁的对象 不可指定,只能this 可指定,默认this
锁的范围 方法体(包括迭代) 代码块(不包括迭代)
适用范围 窄-个别容器 广-所有容器

1.3 同步容器的优缺点

优点

  • 并发编程中,独立操作是线程安全的。比如只是执行一个add,get操作

缺点

  • 性能差,基本上每个操作都进行了加锁
  • 对于复合操作其还不是性能安全的

2.并发容器

并发容器主要是为了用来改善同步容器的性能的,因为同步容器对于每一步操作都进行了加锁,因此导致了同一时刻只能有一个线程访问容器,所以就导致了性能很差。因此为了提升同步容器的性能就引入了并发容器。其将对共享资源在同一时刻的操作由串行改为了并行。
并发容器主要是通过分段锁,非阻塞的CAS算法等技术来对容器进行优化,主要的容器有ConcurrentHashMap、CopyOnWriteArrayList等。
ConcurrentHashMap
它是采用了分段锁的机制,使锁的粒度更小,允许线程并发的进行操作。
CopyOnWriteArrayList
其可以保证线程安全,保证读线程之间不会被阻塞。其是通过Copy-On-Write(COW)思想来实现的,COW即写时复制的思想,也就是在像一个容器中添加元素时,先进行复制原容器,然后在复制的容器中进行添加数据,添加好之后,再指向复制的容器。
可以通过其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();
        
    

其在进行添加元素的时候进行加锁,保证只有一个线程进行数组复制,然后通过复制一个新的列表来进行添加的操作,最后再指向新的列表。进而保证了读写操作不是再同一个数据列表中进行操作的。
从上面的描述我们就可以看出COW会存在内存占用问题。同时其数据一致性也是保证的最终数据一致性。

Java并发机制--同步容器与并发容器

Java并发编程:同步容器
整理自:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3933404.html
1、同步容器出现原因
常用的ArrayList,LinkedList,HashMap,HashSet,Deque等都是线程不安全的;
Java为方便多线程编程,提供了同步容器供用户使用。
2、同步容器类:
2.1:第一类:Vector(ArrayList)、Stack(Vector的子类)、HashTable(HashMap)
2.2:第二类:Collections类中提供的静态工厂方法创建的类。如Collections.synchronizedXxx()获取
3、同步容器的缺陷:
3.1:使用synchronized关键字进行同步降低了效率;
3.2:像Vector这种add和get方法都进行同步,在多线程读时,竞争锁,效率很低。
3.3:多线程操作同步容器时,可能抛出 ConcurrentModificationException异常。
4、ConcurrentModificationException异常原因及解决
4.1:单线程下:Iterator迭代器迭代时,调用了list的remove修改了容器大小,应该调用Iterator的remove方法;
4.2:多线程下:多个线程执行Iterator并修改,此时Iterator线程私有。
改正:【1】获取Iterator时,使用synchronized或者Lock同步。
【2】使用并发容器CopyOnWriteArrayList代替ArrayList和Vector

5、并发容器:java.util.concurrent包
同步容器将所有对容器状态的访问都串行化,保证了安全,却降低了效率;
5.1:ConcurrentHashMap代替同步的Map(Collections.synchronized(new HashMap()),HashMap同步时锁住所有的段,而ConcurrentHashMap只锁住对应的段(segment);
5.2:CopyOnWriteArrayList和CopyOnWriteArraySet分别代替List和Set,写时复制List和写时复制set;
5.3: 其他

以上是关于同步容器与并发容器的主要内容,如果未能解决你的问题,请参考以下文章

同步容器与并发容器

并发编程:同步类容器与并发类容器

Java并发多线程编程——同步容器与并发容器

Java并发多线程编程——同步容器与并发容器

Java学习笔记—多线程(同步容器和并发容器)

Java 多线程 8同步容器与并发容器