看图学源码之 CopyOnWriteArrayList 源码分析

基本简介:

是Java中的一个线程安全的List实现,也是ArrayList 的安全版本,所以就不会有ArrayList并发修改异常 的问题产生了

原理

每次在对 List 进行修改时,创建一个新的副本(即拷贝),而不是直接在原始列表上进行修改。

在创建新的副本时,CopyOnWriteArrayList复制整个内部数组,并在副本上进行修改操作。所以操作是不会被阻塞的,因为读取操作可以同时原始列表新创建的副本上进行。

由于每次修改都会创建一个新的副本,所以CopyOnWriteArrayList适用于读取操作频繁、写入操作较少的场景。它是可以避免读与写之间的竞争条件,从而提供了较好的并发性能。

虽然CopyOnWriteArrayList提供了线程安全的读取操作,但写入操作的性能相对较低,因为每次写入操作都需要复制整个内部数组。因此,如果需要频繁进行写入操作或对 List 的实时性要求较高,可能不适合使用CopyOnWriteArrayList。

总的来说就是:CopyOnWriteArrayList的原理是通过创建副本来实现线程安全的读取操作,避免了读写竞争条件,但写入操作的性能相对较低。

弊端

1、 每次都要赋值,浪费很大的内存空间

2、读线程可能读不到正在写入的数据,是无法保持 数据的实时一致,只能保证数据的最终一致性。

源码解释

public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// 此处是和ArrayList一样的
}

使用的锁

final transient ReentrantLock lock = new ReentrantLock();

装载数组

private transient volatile Object[] array;final Object[] getArray() {return array;
}final void setArray(Object[] a) {array = a;
}

构造器

// 无参构造器
public CopyOnWriteArrayList() {setArray(new Object[0]);
}// 有参构造器
public CopyOnWriteArrayList(Collection<? extends E> c) {Object[] elements;if (c.getClass() == CopyOnWriteArrayList.class)elements = ((CopyOnWriteArrayList<?>)c).getArray();else {elements = c.toArray();if (c.getClass() != ArrayList.class)elements = Arrays.copyOf(elements, elements.length, Object[].class);}setArray(elements);
}
public CopyOnWriteArrayList(E[] toCopyIn) {setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}

get
get(int index)

返回此列表中指定位置的元素。

//返回此列表中指定位置的元素。
public E get(int index) {return get(getArray(), index);
}
//获取数组
final Object[] getArray() {return array;
}
get(Object[] a, int index)

返回此列表中指定位置的元素。

private transient volatile Object[] array;private E get(Object[] a, int index) {return (E) a[index];
}
contains
contains(Object o)

如果此列表包含指定元素,则返回true 。更正式地说,当且仅当此列表包含至少一个元素e且满足(o==null ? e==null : o.equals(e))时,才返回true

在这里插入图片描述

public boolean contains(Object o) {Object[] elements = getArray();// 遍历集合找到返回true 否则返回falsereturn indexOf(o, elements, 0, elements.length) >= 0;
}
containsAll(Collection<?> c)

如果此列表包含指定集合的所有元素,则返回true

在这里插入图片描述

public boolean containsAll(Collection<?> c) {Object[] elements = getArray();int len = elements.length;// 遍历指定集合,在挨个遍历原数组中的元素进行匹配,找不到就返回不存在,falsefor (Object e : c) {if (indexOf(e, elements, 0, len) < 0)return false;}// 否则返回truereturn true;
}

set
set(int index, E element)

将此列表中指定位置的元素替换为指定元素。

在这里插入图片描述

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;// 对原来的数组进行copy,并设置新数组的容量 就是 原数组的容量 Object[] newElements = Arrays.copyOf(elements, len);// 修改新数组指定位置的元素newElements[index] = element;// 新数组 变为 下次getArray 的数组setArray(newElements);} else {// 修改的值和原来的一样,不用修改setArray(elements);}// 返回旧的值return oldValue;} finally {lock.unlock();}
}
add
add(E e)

此方法会添加原数组就有的重复的元素

将指定元素追加到此列表的末尾。

在这里插入图片描述

public boolean add(E e) {final ReentrantLock lock = this.lock;lock.lock();try {// 获取当前数组Object[] elements = getArray();// 当前数组的长度int len = elements.length;// copy 原数组的值到新数组中,并设置新数组的容量是 原数组的容量 + 1 Object[] newElements = Arrays.copyOf(elements, len + 1);// 在新数组的最后位置添加这个元素newElements[len] = e;// 新数组 变为 下次getArray 的数组setArray(newElements);return true;} finally {lock.unlock();}}
add(int index, E element)

此方法会添加原数组就有的重复的元素

在此列表中的指定位置插入指定元素。将当前位于该位置的元素(如果有)和任何后续元素向右移动(将其索引加一)

在这里插入图片描述

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)//copy 原数组的值到新数组中,并设置新数组的容量是 原数组的容量 + 1 newElements = Arrays.copyOf(elements, len + 1);// else {//设置新数组的容量是 原数组的容量 + 1 newElements = new Object[len + 1];// copy 原数组中 指定位置前 和指定位置 后的值到新数组中System.arraycopy(elements, 0, newElements, 0, index);System.arraycopy(elements, index, newElements, index + 1,numMoved);}// 指定位置填充元素newElements[index] = element;// 新数组 变为 下次getArray 的数组setArray(newElements);} finally {lock.unlock();}
}
addAll(Collection<? extends E> c)

此方法会添加原数组就有的重复的元素

将指定集合中的所有元素按照指定集合的迭代器返回的顺序附加到此列表的末尾。

在这里插入图片描述

public boolean addAll(Collection<? extends E> c) {// 转为数组Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();if (cs.length == 0)return false;final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;// 要是原数组长度为 0 并且 添加的集合 是  CopyOnWriteArrayList 或者  ArrayListif (len == 0 && (c.getClass() == CopyOnWriteArrayList.class || c.getClass() == ArrayList.class)) {// 新增的集合就指定为 下次getArray 的数组 setArray(cs);} else {// copy 原数组的值到新数组中,并设置新数组的容量是 原数组的容量 + 新增集合的长度Object[] newElements = Arrays.copyOf(elements, len + cs.length);// 将集合c 元素 copy 到末尾System.arraycopy(cs, 0, newElements, len, cs.length);// 新数组 变为 下次getArray 的数组setArray(newElements);}return true;} finally {lock.unlock();}
}
addAll(int index, Collection<? extends E> c)

此方法会添加原数组就有的重复的元素

将指定集合中的所有元素插入到此列表中,从指定位置开始。将当前位于该位置的元素(如果有)和任何后续元素向右移动(增加它们的索引)

在这里插入图片描述

public boolean addAll(int index, Collection<? extends E> c) {Object[] cs = c.toArray();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);if (cs.length == 0)return false;int numMoved = len - index;Object[] newElements;// 不移动,直接copy 到原来数组的末尾if (numMoved == 0)newElements = Arrays.copyOf(elements, len + cs.length);else {// 指定位置之前和之后的元素拷贝到 新的副本中newElements = new Object[len + cs.length];System.arraycopy(elements, 0, newElements, 0, index);System.arraycopy(elements, index,newElements, index + cs.length,numMoved);}// 在指定位置之后 填充集合元素System.arraycopy(cs, 0, newElements, index, cs.length);// // 新数组 变为 下次getArray 的数组setArray(newElements);return true;} finally {lock.unlock();}
}
addAllAbsent(Collection<? extends E> c)

此方法不会添加原数组就有的重复的元素

将指定集合中尚未包含在此列表中的所有元素按照指定集合的迭代器返回的顺序追加到此列表的末尾。

在这里插入图片描述

public int addAllAbsent(Collection<? extends E> c) {Object[] cs = c.toArray();// 创建一个cs的副本,以防止对原始对象的修改影响到cs变量。if (c.getClass() != ArrayList.class) {cs = cs.clone();}if (cs.length == 0)return 0;final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;int added = 0;  // 不重复元素的个数// uniquify and compact elements in csfor (int i = 0; i < cs.length; ++i) {Object e = cs[i];// 原数组中 和 转为数组的集合中都没有这个元素							if (indexOf(e, elements, 0, len) < 0 &&indexOf(e, cs, 0, added) < 0)//将不重复的元素添加到 cs 数组中。cs[added++] = e;}if (added > 0) {Object[] newElements = Arrays.copyOf(elements, len + added);//不重复元素放到新元素的末尾System.arraycopy(cs, 0, newElements, len, added);setArray(newElements);}// 返回原数组中不存在的集合中的元素的个数return added;} finally {lock.unlock();}
}
// 返回此列表中指定元素第一次出现的索引,如果此列表不包含该元素,则返回 -1。
// 更正式地说,返回满足(o==null ? get(i)==null : o.equals(get(i))) 的最低索引i(第一个位置) ,如果没有这样的索引,则返回 -1 。// 遍历数组找到符合的元素,找不到返回 -1private static int indexOf(Object o, Object[] elements,int index, int fence) {if (o == null) {for (int i = index; i < fence; i++)if (elements[i] == null)return i;} else {for (int i = index; i < fence; i++)if (o.equals(elements[i]))return i;}return -1;}
addIfAbsent(E e)

此方法不会添加原数组就有的重复的元素

追加该元素(如果不存在)。

在这里插入图片描述

public boolean addIfAbsent(E e) {Object[] snapshot = getArray();// 要是元素在原来的数组中是存在的直接返回,否则就调用 addIfAbsent(e, snapshot)return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :addIfAbsent(e, snapshot);
}private boolean addIfAbsent(E e, Object[] snapshot) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] current = getArray();int len = current.length;// 要是数组发生了变化,表示有其他线程正在竞争修改这个数组if (snapshot != current) {// 得到两个数组的公共长度,以小的值为准int common = Math.min(snapshot.length, len);// 遍历两个数组的公共部分for (int i = 0; i < common; i++)// 两个数组又不想等的元素  && 当前数组已经存在要添加的元素if (current[i] != snapshot[i] && eq(e, current[i]))// 不添加return false;// 遍历结束,前面的部分都是一样的// 查找当前数组剩余部分的元素 要是此时能找到添加的元素也 不添加if (indexOf(e, current, common, len) >= 0)return false;}//否则,创建当前数组的副本,并在结尾加上这个元素Object[] newElements = Arrays.copyOf(current, len + 1);newElements[len] = e;setArray(newElements);return true;} finally {lock.unlock();}}
remove
remove(int index)

删除此列表中指定位置的元素。将所有后续元素向左移动(从索引中减去 1)。返回从列表中删除的元素。

在这里插入图片描述

  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;// 不要移动  直接拷贝,长度为原来数组的长度 -1if (numMoved == 0)setArray(Arrays.copyOf(elements, len - 1));else {// 新建副本,副本的长度减少为原来数组的长度 -1// 拷贝到新数组中该位置之前 和 该元素之后 的元素// 新数组 变为 下次getArray 的数组Object[] newElements = new Object[len - 1];System.arraycopy(elements, 0, newElements, 0, index);System.arraycopy(elements, index + 1, newElements, index,numMoved);setArray(newElements);}// 返回旧值return oldValue;} finally {lock.unlock();}}
remove(Object o)

在这里插入图片描述

从此列表中删除第一次出现的指定元素(如果存在)。如果此列表不包含该元素,则它不会更改。更正式地说,删除具有最低索引i元素,使得(o==null ? get(i)==null : o.equals(get(i))) (如果存在这样的元素)。如果此列表包含指定的元素(或者等效地,如果此列表由于调用的结果而更改),则返回true 。

public boolean remove(Object o) {Object[] snapshot = getArray();// 判断原数组是否存在当前 元素,并返回当前元素的位置int index = indexOf(o, snapshot, 0, snapshot.length);// 存在才调用 remove(o, snapshot, index); 移除return (index < 0) ? false : remove(o, snapshot, index);
}
remove(Object o, Object[] snapshot, int index)

给定的最近快照在给定索引处包含 o。

在这里插入图片描述

 private boolean remove(Object o, Object[] snapshot, int index) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] current = getArray();int len = current.length;//  数组发生了变化 if (snapshot != current) findIndex: {// 取删除元素的下标和当前数组长度的最小值 进行 后续遍历int prefix = Math.min(index, len);for (int i = 0; i < prefix; i++) {// 数组中的元素出现了变化  并且  要删除的值就是当前位置的值if (current[i] != snapshot[i] && eq(o, current[i])) {// 更新要删除的元素的下标index = i;// 结束循环break findIndex;}}// 要是现在的元素的下标已经超过要当前的数组的长度if (index >= len)// 删除失败return false;//上面两个数组公共位置的元素都是一样的,那么现在就是当前index位置的元素判断是不是一样的 if (current[index] == o)break findIndex; // 是就结束循环// 从index位置遍历现在数组的元素,直到结束index = indexOf(o, current, index, len);// 要是找不到,删除失败if (index < 0)return false;}// 新建副本,副本的长度减少为原来数组的长度 -1// 拷贝到新数组中该位置之前 和 该元素之后 的元素// 新数组 变为 下次getArray 的数组Object[] newElements = new Object[len - 1];System.arraycopy(current, 0, newElements, 0, index);System.arraycopy(current, index + 1,newElements, index,len - index - 1);setArray(newElements);return true;} finally {lock.unlock();}}
removeRange(int fromIndex, int toIndex)

从此列表中删除索引介于fromIndex (包含)和toIndex (不包含)之间的所有元素。将所有后续元素向左移动(减少它们的索引)。此调用将列表缩短(toIndex - fromIndex)个元素。 (如果toIndex==fromIndex ,则此操作无效。)

在这里插入图片描述

 void removeRange(int fromIndex, int toIndex) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)throw new IndexOutOfBoundsException();// 删除指定范围之后的数组长度int newlen = len - (toIndex - fromIndex);// 需要移动的元素 int numMoved = len - toIndex;// 不用移动,直接拷贝之前的元素if (numMoved == 0)setArray(Arrays.copyOf(elements, newlen));else {// 新建副本,副本的长度减少为原来数组的长度 -1// 拷贝到新数组中该位置之前 和 该元素之后 的元素// 新数组 变为 下次getArray 的数组Object[] newElements = new Object[newlen];System.arraycopy(elements, 0, newElements, 0, fromIndex);System.arraycopy(elements, toIndex, newElements,fromIndex, numMoved);setArray(newElements);}} finally {lock.unlock();}}
removeAll(Collection<?> c)

从此列表中删除指定集合中包含的所有元素。由于需要内部临时数组,因此在此类中这是一个特别昂贵的操作。

在这里插入图片描述

   public boolean removeAll(Collection<?> c) {if (c == null) throw new NullPointerException();final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;if (len != 0) {// temp 数组保存需要保留的元素int newlen = 0;Object[] temp = new Object[len];// 遍历 原数组for (int i = 0; i < len; ++i) {Object element = elements[i];// 要是删除元素的集合不包含这个元素,塞到temp数组中if (!c.contains(element))temp[newlen++] = element;}// 有删除的元素if (newlen != len) {// 将temp copy 作为新数组中setArray(Arrays.copyOf(temp, newlen));return true;}}return false;} finally {lock.unlock();}}
removeIf(Predicate<? super E> filter)

根据指定条件过滤集合元素的功能

在这里插入图片描述

public boolean removeIf(Predicate<? super E> filter) { //Predicate<? super E> 类型的参数 filter,用于过滤集合中的元素if (filter == null) throw new NullPointerException();final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;if (len != 0) {int newlen = 0;Object[] temp = new Object[len];//遍历现在的数组,如果元素不满足 filter 的条件,则将其保存在 temp 数组中,并增加 newlen 的值。for (int i = 0; i < len; ++i) {@SuppressWarnings("unchecked") E e = (E) elements[i];if (!filter.test(e))temp[newlen++] = e;}//有元素被删除if (newlen != len) {// 将temp copy 作为新数组中setArray(Arrays.copyOf(temp, newlen));return true;}}return false;} finally {lock.unlock();}
}
clear

从此列表中删除所有元素。该调用返回后列表将为空。

public void clear() {final ReentrantLock lock = this.lock;lock.lock();try {// 设置一个空的数组作为新的数组setArray(new Object[0]);} finally {lock.unlock();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/205806.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机组成原理—存储系统

文章目录 存储系统分层RAM存储器DRAM刷新地址线复用 ROM存储器疑问 提升主存访问速度双端口RAM多模块存储器单体多字存储器多体并行存储器 主容量扩展cache高速缓存器cache与主存映射&#xff08;读策略&#xff09;替换策略cache写策略 存储方式页式存储器虚拟存储器 计组真的…

【EI会议征稿】第三届密码学、网络安全和通信技术国际会议(CNSCT 2024)

第三届密码学、网络安全和通信技术国际会议&#xff08;CNSCT 2024&#xff09; 2024 3rd International Conference on Cryptography, Network Security and Communication Technology 随着互联网和网络应用的不断发展&#xff0c;网络安全在计算机科学中的地位越来越重要&…

2分钟带你了解什么是Vsync

了解Vsync&#xff0c;首先我们需要认知如下问题 什么是GPUCPU在渲染之前起到什么作用什么是帧&#xff0c;帧率什么是画面撕裂 什么是GPU GPU为图形处理器&#xff0c;又称显示核心、视觉处理器、显示芯片。 是一种专门在个人电脑、工作站、游戏机和一些移动设备(如平板电脑…

C++设计模式——Adapter(适配器)模式

一、Adapter模式是个啥&#xff1f; 适配器模式在软件开发的江湖里&#xff0c;可以说用途是非常广的。下面几个经典的场景来说明适配器模式的使用。 场景一 场景二 假如你正在开发一款股票市场监测程序&#xff0c; 它会从不同来源下载 XML 格式的股票数据&#xff0c; 然后…

软考2018下午第六题改编逻辑(状态模式)

在状态模式中&#xff0c;我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象 package org.example.状态模式.软考航空;/*** author lst* date 2023年12月07日 15:37*/ class FrequentFlyer {CState state;double flyMiles;public FrequentFlyer() {…

【开源】基于JAVA语言的农家乐订餐系统

项目编号&#xff1a; S 043 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S043&#xff0c;文末获取源码。} 项目编号&#xff1a;S043&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户2.2 管理员 三、系统展示四、核…

深圳锐杰金融:用金融力量守护社区健康

深圳市锐杰金融投资有限公司&#xff0c;作为中国经济特区的中流砥柱&#xff0c;近年来以其杰出的金融成绩和坚定的社会责任立场引人注目。然而&#xff0c;这并非一个寻常的金融机构。锐杰金融正在用自己的方式诠释企业责任和慈善精神&#xff0c;通过一系列独特的慈善项目&a…

Leetcode算法题笔记(1)

目录 哈希1. 两数之和1.1 解法11.1 解法2 2. 字母异位词分组2.1 解法12.2 解法2 3. 最长连续序列3.1 解法 小结 双指针4. 移动零4.1 解法14.2 解法2 5. 盛最多水的容器5.1 解法一5.2 解法二 6. 三数之和6.1 解法16.2 解法2 7. 接雨水7.1 解法1 小结 滑动窗口8. 无重复字符的最长…

自然语言处理基础知识 学习

参考&#xff1a;OpenBMB - 让大模型飞入千家万户 【清华NLP】刘知远团队大模型公开课全网首发&#xff5c;带你从入门到实战_哔哩哔哩_bilibili 图灵测试&#xff1a;imitation Game 模仿游戏 Part of speech tagging 词性标注 Named entity recognition &#xff1a; 命名…

[Kubernetes]1.Kubernetes(K8S)介绍,基于腾讯云的K8S环境搭建集群以及裸机搭建K8S集群

一. Kubernetes(K8S)简介 Kubernetes (K8S) 是一个为 容器化应用 提供 集群部署 和 管理 的开源工具,和docker swarm类似,由 Google 开发. Kubernetes 这个名字源于希腊语,意为 “ 舵手 ” 或 “ 飞行员 ” , k8s 这个缩写是因为 k 和 s 之间有八个字符的关系, Google…

Mac电脑统计分析绘图 GraphPad Prism 10 最新 for Mac

GraphPad Prism 10是一款强大的科学数据分析和可视化软件&#xff0c;提供了丰富的统计工具、数据可视化选项和报告生成功能。它使用户能够更轻松地分析和解释实验数据&#xff0c;以支持科学研究和决策。 数据导入和整理&#xff1a;GraphPad Prism 10支持从多种数据源导入数据…

ChatGPT/GPT4科研实践篇: AI绘图+论文写作+编程

1、熟练掌握ChatGPT提示词技巧及各种应用方法&#xff0c;并成为工作中的助手。 2、通过案例掌握ChatGPT撰写、修改论文及工作报告&#xff0c;提供写作能力及优化工作 3、熟练掌握ChatGPT融合相关插件的应用&#xff0c;完成数据分析、编程以及深度学习等相关科研项目。 4、…

Kafka -- 初识

目录 kafka是什么 Topic Partition Broker Cousumer CousumerGroup Offset reblance broker 消息存储 Isr kafka是什么 Kafka 是一个分布式的消息引擎&#xff0c;能够发布和订阅消息流&#xff08;类似于消息队列&#xff09; 以容错的、持久的方式存储消息流 多分区…

MYSQL练题笔记-高级查询和连接-最后一个能进入巴士的人

一、题目相关内容 1&#xff09;相关的表和题目 2&#xff09;帮助理解题目的示例&#xff0c;提供返回结果的格式 二、自己初步的理解 一群人要上巴士但是巴士有体重限制&#xff0c;那只能有限个人才能上去 最后输出这个最后一个上去还不超重的人的名字 我认为首先要缩小…

HarmonyOS 开发基础(二)Image

HarmonyOS 开发基础&#xff08;二&#xff09;Image Entry Component struct Index {// 创建一个状态变量 img 存储 img 网络地址State img: string https://img1.baidu.com/it/u4049022245,514596079&fm253&app138&sizew931&n0&fJPEG&fmtauto?sec1…

创建vue项目:node.js下载安装、配置环境变量,下载安装cnpm,配置npm的目录、镜像,安装vue、搭建vue项目开发环境(保姆级教程一)

今天讲解 Windows 如何创建 vue 项目&#xff0c;搭建 vue 开发环境&#xff0c;这是这个系列的第一章&#xff0c;有什么问题请留言&#xff0c;请点赞收藏&#xff01;&#xff01;&#xff01; 文章目录 一、Vue简单介绍二、开始搭建1、安装node.js环境2、配置npm下载时的默…

1、Redis变慢原因排查(上)

感觉Redis变慢了&#xff0c;这些可能的原因你查了没 &#xff1f;(上) Redis 作为一款业内使用率最高的内存数据库&#xff0c;其拥有非常高的性能&#xff0c;单节点的QPS压测能达到18万以上。但也正因此如此&#xff0c;当应用访问 Redis 时&#xff0c;如果发现响应延迟变…

QT----Visual Studio输入中文报错,常量中有换行符

问题描述 在VS中写qt时发现在标题中输入了中文直接把报错无法运行 解决方法1 修改文件的编码方式。在VS菜单栏 工具->自定义-》命令-》选择文件-》添加高级保存选项命令。 双加选中添加中文的文件&#xff0c;文件-》高级保存选项-》修改utf-8就可以运行了 解决方法2…

AMD 发布新芯片MI300,支持训练和运行大型语言模型

AMD 宣布推出 MI300 芯片&#xff0c;其 Ryzen 8040移动处理器将于2024年用于笔记本电脑。 AMD官方网站&#xff1a;AMD ׀ together we advance_AI AMD——美国半导体公司专门为计算机、通信和消费电子行业设计和制造各种创新的微处理器&#xff08;CPU、GPU、主板芯片组、电…

电脑搜不自己的手机热点,其余热点均可!

一、现象&#xff1a; 之前可正常连接&#xff0c;突然间发现收不到自己的WiFi信号&#xff0c;其余人均可收到。通过重复手机电脑关机、改变热点设置中的频段等方式均没解决&#xff0c;同事电脑和手机可搜索到我的WiFi。 二、问题&#xff1a; WiF驱动程序更新 三&#x…