java并发面试常识之copyonwrite

简介: 今天在网上看到一个问题,问除了加锁之外有没有其他方法来保证线程安全。楼下很多回答copyonwrite机制。这个问题回答有很多,但是copyonwrite的回答有点误导人。

今天在网上看到一个问题,问除了加锁之外有没有其他方法来保证线程安全。楼下很多回答copyonwrite机制。这个问题回答有很多,但是copyonwrite的回答有点误导人。

copyonwrite机制

和单词描述的一样,他的实现就是写时复制, 在往集合中添加数据的时候,先拷贝存储的数组,然后添加元素到拷贝好的数组中,然后用现在的数组去替换成员变量的数组(就是get等读取操作读取的数组)。这个机制和读写锁是一样的,但是比读写锁有改进的地方,那就是读取的时候可以写入的 ,这样省去了读写之间的竞争,看了这个过程,你也发现了问题,同时写入的时候怎么办呢,当然果断还是加锁。

java中的copyonwrite

java中提供了两个利用这个机制实现的线程安全集合。copyonwritearraylist,copyonwritearrayset。看名字就大概猜到他们之间的关系,copyonwritearrayset的底层实现是copyonwritearraylist。我们接下来看看java的实现。

    public E get(int index) {
        return get(getArray(), index);
    }

get的方法就是普通集合的get没有什么特殊的地方,但是成员变量的声明还是有讲究的,是个用volatile声明的数组,这样就保证了读取的那一刻读取的是最新的数据。

private transient volatile Object[] array;

接下来重点就是add方法了。下面的代码可以明显看出是明显需要reentrantlock加锁的,接下来就是复制数据和添加数据的过程,在setArray的过程中,把新的数组赋值给成员变量array(这里是引用的指向,java保证赋值的过程是一个原子操作)。

  public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            Object[] newElements;
            int numMoved = len - index;
            if (numMoved == 0)
                newElements = Arrays.copyOf(elements, len + 1);
            else {
                newElements = new Object[len + 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            newElements[index] = element;
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

关于迭代,他采取的是获取传递给迭代器的数组值进行迭代,中间就算加入新的值也迭代不到。在构造函数中就直接赋值给final的成员变量。

        private final Object[] snapshot;
  
        private COWIterator(Object[] elements, int initialCursor) {
            cursor = initialCursor;
            snapshot = elements;
        }

适用场景

copyonwrite的机制虽然是线程安全的,但是在add操作的时候不停的拷贝是一件很费时的操作,所以使用到这个集合的时候尽量不要出现频繁的添加操作,而且在迭代的时候数据也是不及时的,数据量少还好说,数据太多的时候,实时性可能就差距很大了。在多读取,少添加的时候,他的效果还是不错的(数据量大无所谓,只要你不添加,他都是好用的)。

目录
相关文章
|
3月前
|
算法 Java
50道java集合面试题
50道 java 集合面试题
|
5月前
|
缓存 Java API
Java 面试实操指南与最新技术结合的实战攻略
本指南涵盖Java 17+新特性、Spring Boot 3微服务、响应式编程、容器化部署与数据缓存实操,结合代码案例解析高频面试技术点,助你掌握最新Java技术栈,提升实战能力,轻松应对Java中高级岗位面试。
491 0
|
5月前
|
Java 数据库连接 数据库
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
本文全面总结了Java核心知识点,涵盖基础语法、面向对象、集合框架、并发编程、网络编程及主流框架如Spring生态、MyBatis等,结合JVM原理与性能优化技巧,并通过一个学生信息管理系统的实战案例,帮助你快速掌握Java开发技能,适合Java学习与面试准备。
264 2
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
|
3月前
|
算法 Java
50道java基础面试题
50道java基础面试题
|
5月前
|
缓存 Java 关系型数据库
Java 面试经验总结与最新 BAT 面试资料整理含核心考点的 Java 面试经验及最新 BAT 面试资料
本文汇总了Java面试经验与BAT等大厂常见面试考点,涵盖心态准备、简历优化、面试技巧及Java基础、多线程、JVM、数据库、框架等核心技术点,并附实际代码示例,助力高效备战Java面试。
204 0
|
5月前
|
缓存 Cloud Native Java
Java 面试微服务架构与云原生技术实操内容及核心考点梳理 Java 面试
本内容涵盖Java面试核心技术实操,包括微服务架构(Spring Cloud Alibaba)、响应式编程(WebFlux)、容器化(Docker+K8s)、函数式编程、多级缓存、分库分表、链路追踪(Skywalking)等大厂高频考点,助你系统提升面试能力。
287 0
|
7月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
284 0
|
7月前
|
缓存 安全 Java
【高薪程序员必看】万字长文拆解Java并发编程!(3-1):并发共享问题的解决与分析
活锁:多个线程相互影响对方退出同步代码块的条件而导致线程一直运行的情况。例如,线程1的退出条件是count=5,而线程2和线程3在其代码块中不断地是count进行自增自减的操作,导致线程1永远运行。内存一致性问题:由于JIT即时编译器对缓存的优化和指令重排等造成的内存可见性和有序性问题,可以通过synchronized,volatile,并发集合类等机制来解决。这里的线程安全是指,多个线程调用它们同一个实例的方法时,是线程安全的,但仅仅能保证当前调用的方法是线程安全的,不同方法之间是线程不安全的。
140 0
|
7月前
|
Java 程序员
【高薪程序员必看】万字长文拆解Java并发编程!(3-2):并发共享问题的解决与分析
wait方法和notify方法都是Object类的方法:让当前获取锁的线程进入waiting状态,并进入waitlist队列:让当前获取锁的线程进入waiting状态,并进入waitlist队列,等待n秒后自动唤醒:在waitlist队列中挑一个线程唤醒:唤醒所有在waitlist队列中的线程它们都是之间协作的手段,只有拥有对象锁的线程才能调用这些方法,否则会出现IllegalMonitorStateException异常park方法和unpark方法是LockSupport类中的方法。
156 0