看图学源码之 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 否则返回false
    return indexOf(o, elements, 0, elements.length) >= 0;
}
containsAll(Collection<?> c)

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

在这里插入图片描述

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

// 遍历数组找到符合的元素,找不到返回 -1
 private 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;
          // 不要移动  直接拷贝,长度为原来数组的长度 -1
            if (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();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Agility Robotics 为亚马逊仓库批量生产的人形机器人

一家旨在每年生产 10000 个两足机器人的革命性工厂即将在俄勒冈州塞勒姆成形。 这些机器人由 Agility Robotics 开发&#xff0c;旨在协助亚马逊等行业巨头运输、起重和处理危险货物。 Agility Robotics 表示&#xff0c;其名为 RoboFab 的新制造工厂将成为世界上第一个大规模…

【计算机网络笔记】物理层——频带传输基础

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

Linux_CentOS_7.9 VNC安装卸载以及相关配置开机自启动服务简易记录

VNC安装卸载以及相关配置开机自启动服务&#xff1a; 查看环境&#xff1a;&#xff08;yum镜像源配置可以参考我之前文章里面有详细参考http://t.csdnimg.cn/mzGoI&#xff09; [rootorcl238 ~]# rpm -qa | grep vnc ##查看系统现有VNC软件版本 gtk-vnc2-0.7.0-3.el7.x86…

2023年5个自动化EDA库推荐

EDA或探索性数据分析是一项耗时的工作&#xff0c;但是由于EDA是不可避免的&#xff0c;所以Python出现了很多自动化库来减少执行分析所需的时间。EDA的主要目标不是制作花哨的图形或创建彩色的图形&#xff0c;而是获得对数据集的理解&#xff0c;并获得对变量之间的分布和相关…

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

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

Matlab 用矩阵画图

文章目录 Part.I IntroductionChap.I 预备知识Chap.II 概要Chap.III 杂记 Part.II 用矩阵画图Chap.I 摸索过程Chap.II 绘制专业图Chap.III 矩阵转tiff Part.I Introduction 本文汇总了 Matlab 用矩阵画图的几种方式。 Chap.I 预备知识 关于 *.mat 文件 *.mat文件是 matlab 的…

Java UDP 多人聊天室简易版

服务端 import java.io.*; import java.net.*; import java.util.ArrayList; public class Server{public static ServerSocket server_socket;public static ArrayList<Socket> socketListnew ArrayList<Socket>(); public static void main(String []args){try{…

什么是类加载器?什么是双亲委派模型?

什么是类加载器&#xff0c;类加载器有哪些? 要想理解类加载器的话&#xff0c;务必要先清楚对于一个Java文件&#xff0c;它从编译到执行的整个过程。 类加载器&#xff1a;用于装载字节码文件(.class文件) 运行时数据区&#xff1a;用于分配存储空间 执行引擎&#xff1a;执…

Unity中Batching优化的GPU实例化(2)

文章目录 前言一、GPU实例化的Shader准备步骤1、在Pass中声明实例化需要的变体2、UNITY_VERTEX_INPUT_INSTANCE_ID 在顶点着色器的输入(appdata)和输出(v2f可选)中添加(uint instanceID : SV_InstanceID). 前言 在上篇文章中&#xff0c;我们做了一些GPU实例化的前置准备&…

【OJ比赛日历】快周末了,不来一场比赛吗? #12.09-12.15 #14场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2023-12-09&#xff08;周六&#xff09; #7场比赛2023-12-10…

CodeSys学习笔记

文章目录 1.运动控制的两种方式1.1.SM3_CNC1.2.SM3_Robotics 2.两种运动控制方式的速度、加速度等参数的控制2.1.SM3_CNC2.2.SM3_Robotics 3.CNC的M指令的使用&#xff08;实现&#xff09;逻辑。4.SM3_Robotics中的坐标系5.SM3_Robotics如何实现插补并连续执行&#xff1f; 记…

程序管理与SELinux初探-理论篇

工作管理概念&#xff08;job control&#xff09; 1前台&#xff1a;你可以控制与执行命令的这个环境称为前台 2后台&#xff1a;可以自行运行的工作&#xff0c;你无法ctrlc终止它&#xff0c;可以使用bg/fg调用该工作 3后台中执行的进程不能等待terminal/shell的输入 sui…

MySQL高级--01_1--数据库缓冲池(buffer pool)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 数据库缓冲池(buffer pool)DBMS 会申请占用内存来作为数据缓冲池&#xff0c;在真正访问页面之前&#xff0c;需要把在磁盘上的页缓存到内存中的Buffer Pool 之后才…

【已解决】ImportError: cannot import name ‘Merge‘ from ‘keras.layers‘

问题描述 ImportError: cannot import name ‘Merge‘ from ‘keras.layers‘ 解决办法 1、tensorflow和keras版本要对应&#xff1b; 2、使用"merge" pip uninstall keras pip install keras2.3.1 from keras.layers import merge完结撒花 我这血中带泪的成长&…

35、Django进阶:项目多种数据库配置方式和使用(MySQL、PGSQL、ES、MongoDB、InfluxDB)详解

目录 新建的Django初始内置项目的数据库为什么使用SQLite SQLite的应用场景及优缺点 SQLite的应用场景及优点 SQLite不适用的场景及缺点 为什么使用MySQL Django配置和使用MySQL Django配置和使用PostgreSQL PGSQL的特点 安装与配置PostgreSQL 安装第三方库psycopg2 …

CSM2433 一款集成2.4G+125K 和8位RISC 的SOC芯片

CSM2433是一款集成2.4GHz频段发射器、125KHz接收器和8位RISC&#xff08;精简指令集&#xff09;MCU的SOC芯片。 无线收发器特性&#xff1a; 发射工作在 2.4GHz ISM 频段 发射兼容 BLE 4.2 接收工作在 15KHz-150KHz 内置 32 次可编程 NVM 存储器 3.3V 编程电压 集成低电…

【面试经典150 | 二叉树】对称二叉树

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;递归方法二&#xff1a;迭代 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的…

区块链optimism主网节点搭建

文章目录 官方参考资料编译环境搭建编译Optimism Monorepo编译op-geth 执行下载数据快照生成op-geth和op-node通信密钥op-geth执行脚本 op-node执行脚本 启动日志op-gethop-node 本文是按照官方参考资料基于源码的方式成功搭建optimism主网节点。 官方参考资料 源码&#xff1…

快速排序的非递归实现

上期我们实现了快速排序的递归实现&#xff0c;但是我们知道如果递归深度太深&#xff0c;栈就会溢出&#xff0c;所以我们本期将为大家讲述快速排序的非递归实现&#xff0c;我们需要用到栈的数据结构&#xff0c;我们知道栈中的数据全是在堆区开辟的空间&#xff0c;堆的空间…

大数据分析与应用实验任务十一

大数据分析与应用实验任务十一 实验目的 通过实验掌握spark Streaming相关对象的创建方法&#xff1b; 熟悉spark Streaming对文件流、套接字流和RDD队列流的数据接收处理方法&#xff1b; 熟悉spark Streaming的转换操作&#xff0c;包括无状态和有状态转换。 熟悉spark S…