面试官:换人!赶快换人!连CopyOnWriteArrayList都没听过!确实没听过

这里是码农充电第一站,回复“666”,获取一份专属大礼包

真爱,请设置“星标”或点个“在看”


作者:myseries

来源:cnblogs.com/myseries/p/10877420.html

写入时复制(CopyOnWrite)思想


写入时复制(CopyOnWrite,简称COW)思想是计算机程序设计领域中的一种优化策略。其核心思想是,如果有多个调用者(Callers)同时要求相同的资源(如内存或者是磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者视图修改资源内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此做法主要的优点是如果调用者没有修改资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。

CopyOnWriteArrayList的实现原理


在使用CopyOnWriteArrayList之前,我们先阅读其源码了解下它是如何实现的。以下代码是向CopyOnWriteArrayList中add方法的实现(向CopyOnWriteArrayList里添加元素),可以发现在添加的时候是需要加锁的,否则多线程写的时候会Copy出N个副本出来。

/**

* Appends the specified element to the end of this list.

* @param e element to be appended to this list

* @return 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添加数据,读还是会读到旧的数据,因为写的时候不会锁住旧的CopyOnWriteArrayList。

public E get(int index) {

return get(getArray(), index);

}

JDK中并没有提供CopyOnWriteMap,我们可以参考CopyOnWriteArrayList来实现一个,基本代码如下:

import java.util.Collection;

import java.util.Map;

import java.util.Set;

public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {

private volatile Map<K, V> internalMap;

public CopyOnWriteMap() {

internalMap = new HashMap<K, V>();

}

public V put(K key, V value) {

synchronized (this) {

Map<K, V> newMap = new HashMap<K, V>(internalMap);

V val = newMap.put(key, value);

internalMap = newMap;

return val;

}

}

public V get(Object key) {

return internalMap.get(key);

}

public void putAll(Map<? extends K, ? extends V> newData) {

synchronized (this) {

Map<K, V> newMap = new HashMap<K, V>(internalMap);

newMap.putAll(newData);

internalMap = newMap;

}

}

}

实现很简单,只要了解了CopyOnWrite机制,我们可以实现各种CopyOnWrite容器,并且在不同的应用场景中使用。

几个要点


  • 实现了List接口

  • 内部持有一个ReentrantLock lock = new ReentrantLock();

  • 底层是用volatile transient声明的数组 array

  • 读写分离,写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array

「注:」

volatile (挥发物、易变的):变量修饰符,只能用来修饰变量。volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变 化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

增删改查


1)增

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();

}

}

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();

}

}

2)删

public E remove(int index) {

final ReentrantLock lock = this.lock;

//获得锁

lock.lock();

try {

Object[] elements = getArray();

int len = elements.length;

E oldValue = get(elements, index);

int numMoved = len - index - 1;

if (numMoved == 0)

//如果删除的元素是最后一个,直接复制该元素前的所有元素到新的数组

setArray(Arrays.copyOf(elements, len - 1));

else {

//创建新的数组

Object[] newElements = new Object[len - 1];

//将index+1至最后一个元素向前移动一格

System.arraycopy(elements, 0, newElements, 0, index);

System.arraycopy(elements, index + 1, newElements, index,

numMoved);

setArray(newElements);

}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

技术学习总结

学习技术一定要制定一个明确的学习路线,这样才能高效的学习,不必要做无效功,既浪费时间又得不到什么效率,大家不妨按照我这份路线来学习。

最后面试分享

大家不妨直接在牛客和力扣上多刷题,同时,我也拿了一些面试题跟大家分享,也是从一些大佬那里获得的,大家不妨多刷刷题,为金九银十冲一波!

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
么效率,大家不妨按照我这份路线来学习。

[外链图片转存中…(img-9bFJTOZj-1712531382358)]

[外链图片转存中…(img-dXSNXGrA-1712531382359)]

[外链图片转存中…(img-75vftrRw-1712531382359)]

最后面试分享

大家不妨直接在牛客和力扣上多刷题,同时,我也拿了一些面试题跟大家分享,也是从一些大佬那里获得的,大家不妨多刷刷题,为金九银十冲一波!

[外链图片转存中…(img-r9dvHSlp-1712531382359)]

[外链图片转存中…(img-7jm9m5cm-1712531382359)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值