JUC框架-并发容器源码详解

在这里插入图片描述

文章目录

  • 并发容器
    • ConcurrentHashMap
      • JDK 1.7 及之前的实现原理
      • JDK 1.8 及之后的实现原理
    • CopyOnWriteArrayList & CopyOnWriteArraySet
      • 工作原理(附源码)
      • 特点
      • 适用场景
    • ConcurrentLinkedQueue
      • 数据结构
      • 入队列操作
      • ConcurrentLinkedQueue 特性
      • ConcurrentLinkedQueue 方法
    • BlockingQueue
      • BlockingQueue特性
      • BlockingQueue方法
      • 常见的 BlockingQueue 实现包括
      • 示例
    • ConcurrentSkipListMap和ConcurrentSkipListSet
      • 介绍
      • 内部结构
      • 示例

更多相关内容可查看

并发容器

所谓并发容器就是在多线程环境下能够保证线程安全的容器 , JDK 提供的这些容器大部分在 java.util.concurrent 包中。

ConcurrentHashMap

ConcurrentHashMap 是 Java 并发包 java.util.concurrent 中提供的一个线程安全的 Map 实现。与 HashMap 不同的是,ConcurrentHashMap 采用了分段锁(在 JDK 1.7 及之前)或者无锁化的数据结构(在 JDK 1.8 及之后)来支持高并发的读写操作,并且在并发环境中提供了良好的性能。

JDK 1.7 及之前的实现原理

分段锁实现原理:分段锁(Segment)将hash表进行分段处理,每一段都有一个锁,get的时候可以同时get,put的时候需要分段拿取锁put,实现并发性,扩容也会分段去扩容

JDK 1.8 及之后的实现原理

 public V put(K key, V value) {
        return putVal(key, value, false);
    }

    /** Implementation for put and putIfAbsent */
    final V putVal(K key, V value, boolean onlyIfAbsent) {
        if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode());
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

在 JDK 1.8 及之后的版本中,ConcurrentHashMap 的实现有了较大的改进,采用了基于 Node 的无锁化设计。主要的变化包括:

  • 移除了 Segment:不再使用分段锁的概念,而是直接对 Node 进行操作。
  • 使用 CAS(Compare-and-Swap)操作:在更新数据时,使用 CAS 操作来确保数据的一致性。CAS是一种无锁化的原子操作,它包含三个参数:内存位置(V)、预期的原值(A)和新值(N)。当且仅当 V 的值等于 A 时,才会将 V的值设置为 N。
  • 红黑树:当链表长度超过一定阈值(默认为 8)时,会将链表转换为红黑树,以提高查找性能。

用法跟HashMap一样,只不过相比HashMap而言无需考虑线程安全问题了

CopyOnWriteArrayList & CopyOnWriteArraySet

CopyOnWriteArrayList 是 Java 并发包 java.util.concurrent 中的一个线程安全的列表实现。它的核心思想是在修改操作(如添加、设置或删除元素)时复制底层数组,并在新的副本上执行修改,从而保持原数组对读操作的可见性不变。这种写时复制的策略使得读操作(包括迭代和访问元素)可以无锁地并发进行,因此具有非常好的读性能

工作原理(附源码)

底层数据结构:

CopyOnWriteArrayList 底层使用数组(Object[])来存储元素。这个数组被 volatile
关键字修饰,确保了线程间可见性。

读操作:

读操作(如 get、size、迭代等)是无锁的,它们直接访问底层的 volatile 数组。由于 volatile
的语义,读操作可以确保看到最近一次写操作后的数组状态。
在这里插入图片描述

CopyOnWriteArrayList 提供了COWIterator内部类来实现迭代的线程安全性。当迭代器被创建时,它会获取当前数组的“快照”,并在迭代过程中使用这个快照。即使其他线程修改了列表,也不会影响已经创建的迭代器。在这里插入图片描述

写操作:

    public void replaceAll(UnaryOperator<E> operator) {
        if (operator == null) throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len);
            for (int i = 0; i < len; ++i) {
                @SuppressWarnings("unchecked") E e = (E) elements[i];
                newElements[i] = operator.apply(e);
            }
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }
  • 当执行写操作(如 add、set、remove 等)时,CopyOnWriteArrayList 会先对底层数组进行加锁(通常使用ReentrantLock 或其他锁机制),以防止其他线程同时进行写操作。
  • 接着,它会复制当前数组,创建一个新的数组副本
  • 在新数组副本上执行实际的写操作(添加、更新或删除元素)。
  • 最后,将底层数组的引用指向新的数组副本,并释放锁。这样,后续的读操作将会看到写操作后的新列表状态。

特点

优点

  • 读操作的高性能:由于读操作是无锁的,并且可以直接访问底层数组,因此 CopyOnWriteArrayList 在读密集型场景中通常具有很高的性能。
  • 线程安全:CopyOnWriteArrayList 是线程安全的,可以在多个线程之间共享而无需额外的同步。
  • 迭代时的一致性:在迭代 CopyOnWriteArrayList时,可以看到列表在某一时刻的完整“快照”,而不用担心在迭代过程中列表被其他线程修改。

缺点

  • 写操作的开销:每次修改操作(添加、设置或删除元素)都需要复制底层数组,并在新的副本上执行修改。这会导致写操作的时间复杂度和空间复杂度都是O(n),其中 n 是列表的大小。因此,在写密集型场景中,CopyOnWriteArrayList 的性能可能会非常差。
  • 内存开销:由于每次修改操作都需要创建新的数组副本,因此 CopyOnWriteArrayList 的内存开销可能会比传统的列表实现(如ArrayList)更大。
  • 数据不一致的“最终可见性”:虽然读操作看到的是列表在某一时刻的完整“快照”,但这并不意味着读操作能够立即看到其他线程的最新写操作结果。其他线程的写操作可能需要一些时间才能通过复制底层数组的方式反映到读操作中。

适用场景

CopyOnWriteArrayList 通常适用于读多写少的场景,如缓存、事件监听器等。在这些场景中,读操作的频率远高于写操作,因此 CopyOnWriteArrayList 的高性能读操作可以带来显著的性能提升。然而,在写密集型场景中,应该避免使用 CopyOnWriteArrayList,以免因频繁的数组复制操作而导致性能问题。

ConcurrentLinkedQueue

数据结构

ConcurrentLinkedQueue 由头节点(head)和尾节点(tail)组成,每个节点(Node)包含节点元素(item)和指向下一个节点的引用(next)。节点与节点之间通过 next 关联起来,形成一张链表结构的队列。
初始状态:默认情况下,head节点存储的元素为空,tail节点等于head节点。

入队列操作

public boolean add(E e) {
    return offer(e);
}
    
public boolean offer(E e) {
        checkNotNull(e);
        final Node<E> newNode = new Node<E>(e);

        for (Node<E> t = tail, p = t;;) {
            Node<E> q = p.next;
            if (q == null) {
                // p is last node
                if (p.casNext(null, newNode)) {
                    // Successful CAS is the linearization point
                    // for e to become an element of this queue,
                    // and for newNode to become "live".
                    if (p != t) // hop two nodes at a time
                        casTail(t, newNode);  // Failure is OK.
                    return true;
                }
                // Lost CAS race to another thread; re-read next
            }
            else if (p == q)
                // We have fallen off list.  If tail is unchanged, it
                // will also be off-list, in which case we need to
                // jump to head, from which all live nodes are always
                // reachable.  Else the new tail is a better bet.
                p = (t != (t = tail)) ? t : head;
            else
                // Check for tail updates after two hops.
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }

当元素需要入队时,它们被添加到队列的尾部。假设我们要在一个队列中依次插入4个节点:

  • 添加元素1:队列首先更新head节点的next节点为元素1节点。由于tail节点默认情况下等于head节点,所以它们的next节点都指向元素1节点。
  • 添加元素2:队列设置元素1节点的next节点为元素2节点,然后更新tail节点指向元素2节点。
  • 添加元素3:设置tail节点的next节点为元素3节点(但此时tail节点不指向元素3,因为tail节点是在添加新元素前更新的)。
  • 添加元素4:设置元素3的next节点为元素4节点,然后将tail节点指向元素4节点。

入队操作主要做两件事:一是将入队节点设置成当前队列尾节点的下一个节点;二是更新tail节点。但在入队列前,如果tail节点的next节点不为空,则将入队节点设置成tail节点的next节点;如果tail节点的next节点为空,则将入队节点设置成tail的next节点,所以tail节点不总是尾节点。

可能有点绕,建议反复看几遍

ConcurrentLinkedQueue 特性

  • 无阻塞:由于ConcurrentLinkedQueue基于链表结构实现,因此不会产生阻塞,支持高并发的访问。
  • 多线程安全:ConcurrentLinkedQueue被多个线程共享,可以安全地处理来自多个线程的请求。
  • 高效性:通过CAS(Compare-And-Swap)等算法实现了高效的线程同步,减少了线程之间的等待和竞争,提高了系统的性能。
  • 无界性:ConcurrentLinkedQueue不限制队列的大小,可以动态地增长。但这也意味着需要合理控制生产者的速度,以防止队列无限制地增长

ConcurrentLinkedQueue 方法

  • add(E e) / offer(E e):将指定元素添加到队列的尾部,如果队列满(但实际上 ConcurrentLinkedQueue是无界的)则理论上不会返回false。
  • poll():从队列的头部获取并删除一个元素,如果队列为空则返回null。
  • peek():获取队列头部的元素,但不删除该元素,如果队列为空则返回null。
  • size():获取当前队列中元素的数量(但由于并发性,这个数字可能只是近似值)。
  • isEmpty():判断当前队列是否为空。

不支持空元素:ConcurrentLinkedQueue不支持存储空元素(null)。

BlockingQueue

BlockingQueue 是 Java 并发包 java.util.concurrent 中的一个重要接口,它支持在线程之间传递元素。这个接口是线程安全的,主要用于生产者-消费者模式。BlockingQueue 提供了几种在尝试从队列中检索元素时等待队列变为非空,或者在尝试向队列中添加元素时等待队列空间可用的方法。

BlockingQueue特性

  • 线程安全:多个线程可以安全地访问 BlockingQueue,而无需额外的同步。
  • 插入和移除操作:支持在队列的尾部插入元素(put、offer),在队列的头部移除元素(take、poll)。
  • 容量:BlockingQueue 可以是有界的(如 ArrayBlockingQueue、LinkedBlockingQueue 的带参数构造函数),也可以是无界的(如 LinkedBlockingQueue 的无参数构造函数)。
  • 等待/通知机制:当队列为空时,尝试从队列中取元素的线程会被阻塞,直到有元素可用;当队列满时,尝试向队列中添加元素的线程也会被阻塞,直到队列中有空间可用。

BlockingQueue方法

  • add(E e): 如果可能立即插入元素,则返回 true;否则抛出 IllegalStateException。这个方法在队列满时不会阻塞。
  • offer(E e): 如果可能立即插入元素,则返回 true;否则返回 false。这个方法在队列满时不会阻塞。
  • put(E e): 将元素插入队列,如果队列满,则等待;InterruptedException 将在等待时被抛出。
  • remove(Object o): 从队列中移除指定元素(如果存在)。
  • poll(): 检索并删除队列的头,如果队列为空,则返回 null。
  • take(): 检索并删除队列的头,如果队列为空,则等待;InterruptedException 将在等待时被抛出。
  • peek(): 检索但不删除队列的头;如果队列为空,则返回 null。

常见的 BlockingQueue 实现包括

  • ArrayBlockingQueue:基于数组的、有界阻塞队列。
  • LinkedBlockingQueue:基于链表的、 默认情况下是无界的,但也可以通过指定一个容量限制来创建有界的 LinkedBlockingQueue。
  • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
  • SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等待一个相应的删除操作,反之亦然。

示例

定义两个类:Producer(生产者)和 Consumer(消费者)。生产者将元素添加到队列中,而消费者从队列中取出元素。

import java.util.concurrent.BlockingQueue;  
import java.util.concurrent.LinkedBlockingQueue;  
  
public class ProducerConsumerExample {  
  
    // 创建一个阻塞队列,这里使用LinkedBlockingQueue作为示例  
    private static final BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);  
  
    // 生产者类  
    static class Producer implements Runnable {  
        private final int maxItems;  
  
        public Producer(int maxItems) {  
            this.maxItems = maxItems;  
        }  
  
        @Override  
        public void run() {  
            try {  
                for (int i = 0; i < maxItems; i++) {  
                    System.out.println("Producer produced " + i);  
                    queue.put(i); // 当队列满时,会阻塞  
                    Thread.sleep(100); // 模拟耗时操作  
                }  
            } catch (InterruptedException e) {  
                Thread.currentThread().interrupt();  
            }  
        }  
    }  
  
    // 消费者类  
    static class Consumer implements Runnable {  
        @Override  
        public void run() {  
            while (true) {  
                try {  
                    Integer item = queue.take(); // 当队列空时,会阻塞  
                    System.out.println("Consumer consumed " + item);  
                    Thread.sleep(200); // 模拟耗时操作  
                } catch (InterruptedException e) {  
                    Thread.currentThread().interrupt();  
                    break; // 中断后退出循环  
                }  
            }  
        }  
    }  
  
    public static void main(String[] args) {  
        // 创建并启动生产者线程  
        Thread producerThread = new Thread(new Producer(10));  
        producerThread.start();  
  
        // 创建并启动消费者线程  
        Thread consumerThread = new Thread(new Consumer());  
        consumerThread.start();  
  
        // 注意:这里仅作为示例,实际上可能需要额外的机制来确保消费者线程在所有元素被消费后能够优雅地结束  
    }  
}

在这个例子中,创建了一个 LinkedBlockingQueue 实例,并设置了容量为10。生产者线程生成数字0到9,并将它们添加到队列中。消费者线程从队列中取出数字并打印出来。当队列满时,生产者线程会被阻塞,直到队列中有空间可用;当队列为空时,消费者线程会被阻塞,直到队列中有元素可用。

ConcurrentSkipListMap和ConcurrentSkipListSet

介绍

  • ConcurrentSkipListMap是一个有序的映射表,其中每个键都映射到一个值。
  • 它使用跳表数据结构来实现,跳表是一种通过维护多个有序链表来提供快速查找、插入和删除操作的数据结构。
  • ConcurrentSkipListMap的设计目标是提供接近于O(1)的平均时间复杂度,对于常见的操作如get、put、remove等。

内部结构

  • ConcurrentSkipListMap内部使用三个内部类来构建跳表结构:Node、Index、HeadIndex。
  • 每个节点(Node)都包含两个指针,一个指向同一链表中的下一个元素,另一个指向下面一层的元素。
  • 索引节点(Index)和头索引节点(HeadIndex)用于加快查找过程,通过它们可以快速定位到包含目标元素的链表层级

示例

跳表内的所有链表的元素都是排序的。查找时,可以从顶级链表开始找。一旦发现被查找的元素大于当前链表中的取值,就会转入下一层链表继续找。这也就是说在查找过程中,搜索是跳跃式的。如上图所示,在跳表中查找元素 25 查找 25 的时候原来需要遍历 9 次,现在只需要 5 次即可。针对链表长度比较大的时候,构建索引查找效率的提升就会非常明显。
从上面很容易看出,跳表是一种利用空间换时间的算法。
使用跳表实现 Map 和使用哈希算法实现Map的另外一个不同之处是:哈希并不会保存元素的顺序,而跳表内所有的元素都是排序的。因此在对跳表进行遍历时,你会得到一个有序的结果

注:虽然ConcurrentSkipListMap提供了线程安全,但在高并发场景下,仍然需要注意竞态条件和其他并发问题。对于大数据量的应用场景,ConcurrentSkipListMap的性能可能不如其他并发数据结构(如ConcurrentHashMap),因为它需要维护额外的跳表结构。
ConcurrentSkipListSet 是 Java 并发包(java.util.concurrent)中的一个类,它实现了 NavigableSet 接口,并提供了线程安全的、基于跳跃列表(SkipList)的无序集合。然而,需要注意的是,尽管 ConcurrentSkipListSet 的元素是无序的(插入顺序不保证),但它维护了元素的自然顺序或根据创建集合时提供的 Comparator 进行排序

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

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

相关文章

深入分析 Android Activity (八)

文章目录 深入分析 Android Activity (八)1. Activity 的资源管理1.1 使用资源 ID1.2 动态加载资源1.3 资源的本地化1.4 使用 TypedArray 访问资源 2. Activity 的配置变更处理2.1 在 Manifest 文件中声明配置变更2.2 重写 onConfigurationChanged 方法2.3 保存和恢复实例状态 …

MySQL--InnoDB体系结构

目录 一、物理存储结构 二、表空间 1.数据表空间介绍 2.数据表空间迁移 3.共享表空间 4.临时表空间 5.undo表空间 三、InnoDB内存结构 1.innodb_buffer_pool 2.innodb_log_buffer 四、InnoDB 8.0结构图例 五、InnoDB重要参数 1.redo log刷新磁盘策略 2.刷盘方式&…

联想应用商店开发者常见问题FAQ

Phone/Pad应用常见问题 应用上传FAQ Q. 上传apk包时&#xff0c;提示“该包名已存在”如何处理&#xff1f; A&#xff1a;若应用包名出现冲突&#xff0c;请先核实该账号是否已存在该包名产品&#xff0c;若不在该账号下&#xff0c;请进行应用认领。 Q. 应用是否可以授权…

计算机网络——TCP / IP 网络模型

OSI 七层模型 七层模型是国际标准化的一个网络分层模型&#xff0c;大体结构可以分成七层。每层提供不同的功能。 图片来源 JavaGuide 但是这样七层结构比较复杂&#xff0c;不太实用&#xff0c;所以有了 TCP / IP 模型。 TCP / IP 网络模型 TCP / IP 网络模型可以看作是 O…

Overall Accuracy(OA)、Average Accuracy(AAcc)计算公式

四个重要的指标&#xff1a; True Positive&#xff08;TP&#xff09;、False Positive&#xff08;FP&#xff09;、True Negative&#xff08;TN&#xff09;和False Negative&#xff08;FN&#xff09;。 TP表示分类器预测结果为正样本&#xff0c;实际也为正样本&#xf…

第16篇:JTAG UART IP应用<三>

Q&#xff1a;如何通过HAL API函数库访问JTAG UART&#xff1f; A&#xff1a;Quartus硬件工程以及Platform Designer系统也和第一个Nios II工程--Hello_World的Quartus硬件工程一样。 Nios II软件工程对应的C程序调用HAL API函数&#xff0c;如open用于打开和创建文件&#…

感觉是通俗易懂的大模型入门(一)

最近人工智能非常火爆,大家可能经常听到AI、深度学习、大语言模型等名词。但真正能够将它们拆开来细致讲解的内容并不多。我大学就是学这个的,毕业后一直从事这个领域的工作。所以我打算今年陆续做一些这方面的科普,也借此机会复习巩固一下自己的知识体系。 今天就算是第一期,…

POLYGON - Elven Realm - Low Poly 3D Art by Synty(低多边形精灵王国)

Synty Studios™展示:POLYGON-精灵王国 精灵王国隐藏在群山之间,远离非魔法生物的控制。 精灵人以符文之花为动力,将其作为病房、电源、武器附魔和连接他们陆地之间的门户。 主要功能 -700多项独特资产 -模块化建筑系统,包括悬崖和瀑布。 -包括详细的演示场景 资产 角色(x…

基于Cortex的MCU设计

基于Cortex的MCU设计 今日更新的存货文档&#xff0c;发现日更文章还是很花时间的。保证一周更新三篇文章就行啦&#xff0c;本篇文章的内容起始主要取自于《Cortex-M3 权威指南》和知网下载的论文。写的不详细&#xff0c;想进一步了解的就去看这篇文档或网上找别的资料&#…

mysql实战——mysql5.7保姆级安装教程

1、上传 上传5.7压缩包到/usr/local目录下 2、解压 cd /usr/local tar -zxvf mysql--5.7.38-linux-glibc2.12-x86_64.tar.gz mv mysql-5.7.38-linux-glibc2.12-x86_64/ mysql 3、创建mysql用户组和用户 groupadd mysql useradd -g mysql mysql 4、创建数据目录data&#xf…

如何设置远程桌面连接?

远程桌面连接是一种方便快捷的远程访问工具&#xff0c;可以帮助用户在不同地区间快速组建局域网&#xff0c;解决复杂网络环境下的远程连接问题。本文将针对使用远程桌面连接的操作步骤进行详细介绍&#xff0c;以帮助大家快速上手。 步骤一&#xff1a;下载并安装远程桌面连接…

柳宗元,政治坎坷与文学辉煌的交织

&#x1f4a1; 如果想阅读最新的文章&#xff0c;或者有技术问题需要交流和沟通&#xff0c;可搜索并关注微信公众号“希望睿智”。 柳宗元&#xff0c;字子厚&#xff0c;生于唐代宗大历年间&#xff08;公元773年&#xff09;&#xff0c;卒于唐宪宗元和年间&#xff08;公元…

Python批量docx或doc文档转换pdf

说明&#xff1a; 1、因为项目需要&#xff0c;需要手动将十几个word文档转换成pdf文档 2、python请安装3.9.0以上&#xff0c;否则一些依赖库无法正常用 #! /usr/bin/python3 # -*- coding: utf-8 -*-import os import comtypes.client# 批量将docx文件转换pdf文件 def docx_t…

OpenBMC相关的网站

openbmc官方网站 https://github.com/openbmchttps://github.com/openbmc Dashboard [Jenkins]https://jenkins.openbmc.org/ https://gerrit.openbmc.org/Gerrit Code Reviewhttps://gerrit.openbmc.org/ Searchhttps://grok.openbmc.org/ openbmc参考网站 https://www.c…

脱产二战Top3:终将梦校纳入囊中!

这个系列会邀请上岸学长学姐进行经验分享~ 今天分享经验的同学是小马哥819全程班的学员&#xff0c;二战高分上岸上海交通大学&#xff01; 经验分享 在去年考研上交失利后&#xff0c;我选择了在家脱产二战一年&#xff0c;所幸还算取得了比较理想的结果。 我本科中部地区…

攒粒是什么?怎么用攒粒赚钱?

攒粒简介 攒粒的前身是91问问&#xff0c;隶属于上海道道永泉市场调查有限公司&#xff0c;是一家专业的全球在线调研服务公司&#xff0c;也是是国内排名前列的社区调查之一&#xff0c;10年在线调研&#xff0c;600万会员亲身体验&#xff0c;提供网络调查、市场调查、问卷调…

KAN(Kolmogorov-Arnold Network)的理解 1

系列文章目录 第一部分 KAN的理解——数学背景 文章目录 系列文章目录前言KAN背后的数学原理&#xff1a;Kolmogorov-Arnold representation theorem 前言 这里记录我对于KAN的探索过程&#xff0c;每次会尝试理解解释一部分问题。欢迎大家和我一起讨论。 KAN tutorial KAN背…

如何从 Android 恢复已删除的相机照片?(7 种行之有效的方法)

如今&#xff0c;随着智能手机的不断创新和突破&#xff0c;我们可以毫不费力地用安卓手机相机拍摄高清照片。然而&#xff0c;随着安卓手机中相机照片的积累&#xff0c;有时我们可能会因为各种原因丢失这些相机照片。那么如何从安卓设备恢复已删除的相机照片就成了困扰许多人…

Megatron-LM源码系列(八): Context Parallel并行

1. Context Parallel并行原理介绍 megatron中的context并行(简称CP)与sequence并行(简称SP)不同点在于&#xff0c;SP只针对Layernorm和Dropout输出的activation在sequence维度上进行切分&#xff0c;CP则是对所有的input输入和所有的输出activation在sequence维度上进行切分&…

06.部署jpress

安装mariadb数据 yum -y install mariadb-server #启动并设置开启自启动 systemctl start mariadb.service systemctl enable mariadb.service数据库准备 [rootweb01 ~]# mysql Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id…