首页 > 其他分享 >同步容器与并发容器

同步容器与并发容器

时间:2023-05-07 12:14:37浏览次数:28  
标签:容器 同步 return synchronized 并发 boolean mutex public

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会存在内存占用问题。同时其数据一致性也是保证的最终数据一致性。

标签:容器,同步,return,synchronized,并发,boolean,mutex,public
From: https://www.cnblogs.com/mcj123/p/17352373.html

相关文章

  • 【java】javamail+freemarker生成邮件模板,并发送邮件
    一、前言      在上一篇博客中小编向大家介绍了发送带附件的邮件,实践一下也是不错的。这一篇博客是为下一篇博客进行铺垫的,因为项目中需要一个推送的功能,要把推送的信息灵活的显示到一个固有的模板上。所以为了达到这个目的,小编就引入了freemarker。下面向大家介绍。二、What......
  • Flink Cdc MySQL 整库同步到 StarRocks
    这段时间开始调研使用StarRocks做准实时数据仓库:flinkcdc实时同步数据到StarRocks,然后在StarRocks中做分层计算,直接把StarRocks中的ADS层提供给BI查询。架构如下:由于用到的表比较多,不能用FlinkSQL给每个表都做个CDC的任务(任务太多不好维护、对数据库又可能有......
  • 【SSH】SSH框架(三)——Spring IOC和AOP的容器框架
    一、前言      在上一篇博客《【SSH】SSH框架(二)——Struts2框架的简单认识》中小编向大家简单的介绍了SSH框架中的第一个S,也就是Struct2。接下来小编将会向大家分享第二个S——Spring。二、Spring是干什么用的?      其实在小编以前做过的项目中也使用过Spring,那个时候......
  • spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件)
    关键字:spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件)应用场景:很多时候我们想要在某个类加载完毕时干某件事情,但是使用了spring管理对象,我们这个类引用了其他类(可能是更复杂的关联),所以当我们去使用这个类做事情时发现包空指针错误,这是因为我们......
  • vscode增量文本同步更新
    笔记软件在2023/5/611:04:17推送该笔记onDidOpenTextDocument:当文件打开后调用onDidChangeTextDocument:当文本变动后调用onDidCloseTextDocument:当文件关闭后调用connection.onInitialize((params):InitializeResult=>{...return{capabilities:{......
  • canal数据库数据备份时,不同步删除分区动作。
        场景:我们有一大批业务数据,为减少数据库压力和提高使用效率,需要周期性的清理历史分区数据。但是这些数据并不是代表不要了。需要在另一个地方保存 传统数据库的主从复制功能,主要强调的是可以互为备份,强调高可用和可读写分离。但是在我们这个场景下并不适用......
  • VS2022创建.NET Framework MVC 并发布到IIS
     创建 发布 设置完成后点击发布 部署IIS ......
  • Go笔记(十五):并发编程
    一、协程的创建Go语言支持并发,只需要通过go关键字来开启goroutine(协程)即可。goroutine(协程)是轻量级线程,goroutine(协程)的调度是由Golang运行时进行管理的。goroutine语法格式(创建协程):go函数名(参数列表)示例代码如下:1packagemain2imp......
  • python基础学习-容器
    """容器的通用功能:len(容器)统计容器元素个数max(容器)统计最大元素min(容器)统计最小元素类型转换list(容器)给定容器转换为liststr(容器)给定容器转换为strtuple(容器)给定容器转换为tupleset(容器)给定容器转换set通用排序:sorted(容器,[reverse=True])......
  • JAVA中的两个容器StringBuilder和StringJoiner概述
    JAVA中的两个容器StringBuilder和StringJoiner概述StringBuilder可以看成一个容器,创建之后里面的内容是可以修改的方法名说明publicStringBuilderappend(任意类型)添加数据,并返回对象本身publicStringBuilderreverse()反转容器中的内容publicintlength()返......