Java 多线程之线程安全集合

文章目录

    • 一、概述
    • 二、List 接口线程安全实现类
      • 2.1 普通 List 变线程安全 List
      • 2.2 Vector
      • 2.3 CopyOnWriteArrayList
    • 三、Map 接口线程安全实现类
      • 3.1 普通 Map 变线程安全 Map
      • 3.2 Hashtable
      • 3.3 ConcurrentHashMap
      • 3.4 ConcurrentSkipListMap 有序/跳表
    • 四、Set 接口线程安全实现类
      • 4.1 普通 Set 变线程安全 Set
      • 4.2 CopyOnWriteArraySet
      • 4.3 ConcurrentSkipListSet 有序/跳表
    • 五、Queue 接口线程安全实现类
      • 5.1 ConcurrentLinkedQueue 非阻塞/链表
      • 5.2 LinkedBlockingQueue 阻塞/链表
      • 5.3 ArrayBlockingQueue 阻塞/数组
      • 5.4 PriorityBlockingQueue 阻塞/数组
      • 5.5 DelayQueue 阻塞/时间排序
      • 5.6 SynchronousQueue 一对一同步
      • 5.7 LinkedTransferQueue 多对多同步

一、概述

  • 集合关系图
    在这里插入图片描述

  • 本文主要关注线程安全的集合,如 List、Set、Queue、Map 等接口的线程安全的实现方式,有关集合基础知识请转到这里。所谓线程安全集合,就是在多线程环境中使用集合不会导致数据不一致和数据异常的集合。在 Java 中线程安全集现在基本都使用 java.util.concurrent 包下的类。

二、List 接口线程安全实现类

2.1 普通 List 变线程安全 List

  • 要想普通集合能在多线程环境中使用,最简单的办法就是使用 Collections.synchronizedList 方法,将普通集合转换为线程安全集合。当然也可以手动加锁,如 synchronized。

  • 如 ArrayList 本身不是线程安全的,要使他具有线程安全,可以使用 Collections.synchronizedList(new ArrayList<>()) 。接下来的方法与使用普通 List 并不区别。

    List<Object> list = Collections.synchronizedList(new ArrayList<>());
    // 添加元素
    arrayList.add("aaa");
    arrayList.add("bbb");
    arrayList.add("ccc");
    
  • 通过查看 Collections.synchronizedList 的源码片段,可以看出本质上就是使用 synchronized 加锁。

    public class Collections {// 省略大部分代码 ...public static <T> List<T> synchronizedList(List<T> list) {return (list instanceof RandomAccess ?new SynchronizedRandomAccessList<>(list) :new SynchronizedList<>(list));}// 省略大部分代码 ...static class SynchronizedList<E>extends SynchronizedCollection<E>implements List<E> {public E get(int index) {synchronized (mutex) {return list.get(index);}}public E set(int index, E element) {synchronized (mutex) {return list.set(index, element);}}public void add(int index, E element) {synchronized (mutex) {list.add(index, element);}}public E remove(int index) {synchronized (mutex) {return list.remove(index);}}// 省略大部分代码 ...}
    }    
    
  • 当然, Map 和 Set 类型也一样,同样可以使用 Collections 将其转成线程案例的。

            Map<Object, Object> map = Collections.synchronizedMap(new HashMap<>());Set<Object> set = Collections.synchronizedSet(new HashSet<>());
    

2.2 Vector

  • Vector 是线程安全的,他的实现方式是给每个方法加 synchronized。因为他是早期的实现方式,某些情况下效率较低,所以一般情况下不会选用。

            // 创建一个 VectorVector<String> vector = new Vector<>();// 添加元素vector.add("aaa");vector.add("bbb");vector.add("ccc");
    
  • Vector 线程安全实现源码片段,可以看出是在每个方法上加 synchronized。

    public class Vector<E>extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {// 省略大部分代码 ...public synchronized boolean add(E e) {modCount++;ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = e;return true;}    public synchronized E get(int index) {if (index >= elementCount)throw new ArrayIndexOutOfBoundsException(index);return elementData(index);}// 省略大部分代码 ...
    }

    Vector 还有一个字类 Stack 也是线程安全的。是一个后进先出(LIFO)的数据结构,它支持在栈顶进行插入、删除和查找操作。由于其同步特性,可能会导致在高并发场景下的性能相对较低。在现代Java编程中,更常见的做法是使用 LinkedList 或 ArrayDeque 等非线程安全的实现,并在需要时通过显式的同步手段保证线程安全。这样可以在需要时灵活地选择适合具体场景的同步策略。

2.3 CopyOnWriteArrayList

  • CopyOnWriteArrayList 是线程安全的,基于数组操作,效率高,他适用于读多写少场景下。

            // 创建一个 CopyOnWriteArrayListList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();// 添加元素copyOnWriteArrayList.add("aaa");copyOnWriteArrayList.add("bbb");copyOnWriteArrayList.add("ccc");
    
  • 从 CopyOnWriteArrayList 源码片段,可以看出他在 add 时先加锁,然后复制一份内容出来,在复制的内容上进行添加操作,然后再把数据引用指向复制的内容上。

public class CopyOnWriteArrayList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {final transient ReentrantLock lock = new ReentrantLock();private transient volatile Object[] array;final Object[] getArray() {return array;}final void setArray(Object[] a) {array = a;}// 省略大部分代码 ...  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 E get(int index) {return get(getArray(), index);}// 省略大部分代码 ...
}   

三、Map 接口线程安全实现类

3.1 普通 Map 变线程安全 Map

  • 要想使普通 Map 能在多线程环境中使用,最简单的办法就是使用 Collections.synchronizedMap 方法,将普通 Map 转换为线程安全Map 。当然也可以手动加锁,如 synchronized。

  • 如 HashMap 本身不是线程安全的,要使他具有线程安全可以使用 Collections.synchronizedMap(new HashMap<>()) 。然后使用时也使用普通 Map 并不差别。

    Map<Object, Object> map = Collections.synchronizedMap(new HashMap<>());
    // 添加键值对
    map.put("aaa", 1);
    map.put("bbb", 2);
    map.put("ccc", 3);SortedMap<String, String> sortedMap = new TreeMap<>();
    SortedMap<String, String> synchronizedSortedMap = Collections.synchronizedSortedMap(sortedMap);
    

Google Guava 库中提供了线程安全的 Map 实现的 CopyOnWriteMap。

3.2 Hashtable

  • Hashtable 是一个线程安全的,他类似于 Vector 一样,在所有方法上加锁。

    // 创建一个 Hashtable
    Map<String, Integer> hashtable = new Hashtable<>();
    // 添加键值对
    hashtable.put("aaa", 1);
    hashtable.put("bbb", 2);
    hashtable.put("ccc", 3);
    
  • 这是早期的产品,全部方法自带锁(synchronized),现在一般不用,要用一般也是将 HashMap 转成线程安全 Map。

3.3 ConcurrentHashMap

  • ConcurrentHashMap 是多线程环境下,效率最高的 Map。

    //创建一个 ConcurrentHashMap
    ConcurrentHashMap map = new ConcurrentHashMap ();
    //添加元素
    map.put("aaa", "111");
    map.put("bbb", "aaa");
    map.put("ccc", "aaa");
    

3.4 ConcurrentSkipListMap 有序/跳表

  • ConcurrentSkipListMap 是支持排序的并发集合,基于跳表(Skip List)数据结构实现。它能够在多线程并发环境中提供高效的有序映射。跳表的结构允许快速查找、插入和删除操作,同时保持有序性。

    基于 HashMap 数据结构实现的 ConcurrentHashMap 表现在读效率高。TreeMap 基于红黑树实现的,能够排序。

    在Redis 的 sorted_set 实现算法也是跳表

    // 创建一个默认按照自然顺序排序的 ConcurrentSkipListMap
    ConcurrentSkipListMap<Integer, String> skipListMap = new ConcurrentSkipListMap<>();
    // 添加键值对
    skipListMap.put(3, "Three");
    skipListMap.put(1, "One");
    skipListMap.put(2, "Two");
    

四、Set 接口线程安全实现类

4.1 普通 Set 变线程安全 Set

  • 要想使普通 Set 能在多线程环境中使用,最简单的办法就是使用 Collections.synchronizedSet 方法,将普通 Set 转换为线程安全Set。当然也可以手动加锁,如 synchronized。

  • 如 HashSet 本身不是线程安全的,要使他具有线程安全,可以使用 Collections.synchronizedSet(new HashSet<>()) 。接下来的方法与使用普通 Set 并不区别。

    Set<Object> set = Collections.synchronizedSet(new HashSet<>());
    set.add("aaa");
    

4.2 CopyOnWriteArraySet

  • CopyOnWriteArraySet 是 java.util.concurrent 包中提供的线程安全的 Set 实现。它使用 CopyOnWriteArrayList 来支持并发访问,特别适合读多写少的场景。

    Set<String> set = new CopyOnWriteArraySet<>();
    set.add("aaa");
    

4.3 ConcurrentSkipListSet 有序/跳表

  • ConcurrentSkipListSet 是 java.util.concurrent 包中基于跳表的实现,提供了有序的、线程安全的 Set 。与 ConcurrentSkipListMap 类似,它基于跳表数据结构,支持并发访问,并具有良好的性能特性。
Set<String> set = new ConcurrentSkipListSet<>();
set.add("aaa");

五、Queue 接口线程安全实现类

5.1 ConcurrentLinkedQueue 非阻塞/链表

  • ConcurrentLinkedQueue 是 java.util.concurrent 包中提供的非阻塞线程安全队列实现。它基于链表结构,适用于高并发的场景。

  • ConcurrentLinkedQueue 主要方法说明

    • peek() 获取队列中的值,但不从队列中删除该值。
    • poll() 获取队列中的值,且从队列中删除该值。
    • offer() 等同于 add 方法,都是向队列中添加数据。
    ConcurrentLinkedQueue<String> concurrentLinkedQueue = new ConcurrentLinkedQueue<>();
    // 添加元素
    concurrentLinkedQueue.offer("aaa");
    concurrentLinkedQueue.offer("bbb");
    concurrentLinkedQueue.offer("ccc");
    // 获取并移除队列头部元素
    String headElement = concurrentLinkedQueue.poll();
    // 获取队列头部元素(不移除)
    String peekedElement = concurrentLinkedQueue.peek();
    // 遍历元素
    for (String item : concurrentLinkedQueue) {System.out.println(item);
    }
    // 检查元素是否存在
    boolean containsBanana = concurrentLinkedQueue.contains("Banana");
    // 获取队列大小
    int size = concurrentLinkedQueue.size();
    // 清空队列
    concurrentLinkedQueue.clear();
    // 检查是否为空
    boolean isEmpty = concurrentLinkedQueue.isEmpty();
    

5.2 LinkedBlockingQueue 阻塞/链表

  • LinkedBlockingQueue 提供阻塞线程安全队列实现。它基于链表结构,可以在队列为空或已满时进行阻塞。

    LinkedBlockingQueue<String> linkedBlockingQueue = new LinkedBlockingQueue<>();
    // 添加元素
    linkedBlockingQueue.offer("aaa");
    linkedBlockingQueue.offer("bbb");
    linkedBlockingQueue.offer("ccc");
    // 获取并移除队列头部元素
    try {String headElement = linkedBlockingQueue.take();
    } catch (InterruptedException e) {e.printStackTrace();
    }
    

5.3 ArrayBlockingQueue 阻塞/数组

  • ArrayBlockingQueue 提供阻塞线程安全队列实现。它基于数组结构,也可以在队列为空或已满时进行阻塞。

  • ArrayBlockingQueue 指定了长度的队列,基于数组,效率更高。

    • put() 方法向队列中添加元素,如果队列满了,则阻塞添加操作。

    • take() 方法从队列中获取元素,如果队列空了,则阻塞获取操作。

      通过这两个方法可以实现了生产者、消费者模型。

    package top.yiqifu.study.p002_collections;import java.util.concurrent.ArrayBlockingQueue;public class Test153_ArrayBlockingQueue {public static void main(String[] args) {// 创建一个 ArrayBlockingQueue,指定容量为3int capacity = 3;ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(capacity);// 消费(获取)数据线程Thread thread1 = new Thread(() -> {for (int i = 1; i <= 100; i++) {try {// 获取并移除队列头部元素String headElement = arrayBlockingQueue.take();System.out.println("获取数据 " + headElement);Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}});thread1.start();// 生产(添加)数据线程Thread thread2 = new Thread(() -> {for (int i = 1; i <= 100; i++) {try {// 尝试添加元素(如果队列已满,会被阻塞)arrayBlockingQueue.put("data " + i);System.out.println("添加数据 " + i);} catch (InterruptedException e) {e.printStackTrace();}}});thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}}
    }

5.4 PriorityBlockingQueue 阻塞/数组

  • PriorityBlockingQueue 阻塞线程安全队列实现,具有优先级排序。元素按照它们的自然顺序或根据构造函数提供的比较器进行排序。

    package top.yiqifu.study.p002_collections;import java.util.concurrent.*;public class Test154_PriorityBlockingQueue {public static void main(String[] args) {PriorityBlockingQueue<String> priorityBlockingQueue = new PriorityBlockingQueue<>();// 添加元素priorityBlockingQueue.offer("bbb");priorityBlockingQueue.offer("ccc");priorityBlockingQueue.offer("aaa");// 遍历元素,遍历不一定有序,遍历不移除for (String item : priorityBlockingQueue) {System.out.println(item);}// 获取并移除队列头部元素(take 按照自然排序顺序)while (priorityBlockingQueue.size()>0) {try {String headElement = priorityBlockingQueue.take();System.out.println("take=" + headElement);} catch (InterruptedException e) {e.printStackTrace();}}}
    }

5.5 DelayQueue 阻塞/时间排序

  • DelayQueue 提供阻塞线程安全队列实现,用于按照指定的延迟时间对元素进行排序。即按等待时间排序,使用 PriorityQueue 实现。

    PriorityQueue 是具有排序功能(使用二叉树排序)的队列

  • DelayQueue 中,使用 take 方法时,按等待时间短的先返回。即可以按时间进行任务调度。

    package top.yiqifu.study.p002_collections;import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.DelayQueue;
    import java.util.concurrent.Delayed;
    import java.util.concurrent.TimeUnit;public class Test155_DelayQueue {public static void main(String[] args) throws InterruptedException {// 创建一个 DelayQueueBlockingQueue<DelayedElement> delayQueue = new DelayQueue<>();// 添加延迟元素delayQueue.put(new DelayedElement("Element1", 3000));delayQueue.put(new DelayedElement("Element2", 2000));delayQueue.put(new DelayedElement("Element3", 5000));// 遍历元素System.out.println("遍历元素:");for (DelayedElement element : delayQueue) {System.out.println(element);}System.out.println();// 获取队列头部元素(不移除)DelayedElement peekedElement = delayQueue.peek();System.out.println("获取队列头部元素(不移除): " + peekedElement);System.out.println();while (delayQueue.size()>0) {// 获取并移除队列头部元素(按照剩余延迟时间排序)try {DelayedElement headElement = delayQueue.take(); // 注意这个方法在时间没到时会阻塞System.out.println("Taked Head Element: " + headElement);} catch (InterruptedException e) {e.printStackTrace();}}}static class DelayedElement implements Delayed {private String name;private long delayTime;public DelayedElement(String name, long delayTime) {this.name = name;this.delayTime = System.currentTimeMillis() + delayTime;}@Overridepublic long getDelay(TimeUnit unit) {long diff = delayTime - System.currentTimeMillis();return unit.convert(diff, TimeUnit.MILLISECONDS);}@Overridepublic int compareTo(Delayed o) {return Long.compare(this.delayTime, ((DelayedElement) o).delayTime);}@Overridepublic String toString() {return "DelayedElement{" +"name='" + name + '\'' +", delayTime=" + delayTime +'}';}}
    }

5.6 SynchronousQueue 一对一同步

  • SynchronousQueue 是一种特殊的队列,它的大小始终为 0。由于其特殊性,SynchronousQueue 并不会存储元素,而是在生产者和消费者之间进行直接传递。其安全性是通过直接传递而非存储实现的。

    没有容量的队列

    不能 add ,只能 put

  • 作用:把一个数据传递到另一个线程。线程间数据交换。有点类似于 Exchanger。

    package top.yiqifu.study.p002_collections;import java.util.concurrent.SynchronousQueue;public class Test156_SynchronousQueue {public static void main(String[] args)  {// 创建一个 SynchronousQueueSynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();// 启动消费者线程new Thread(() -> {for (int i = 1; i < 10; i++) {try {System.out.println("准备消费"+i);String message = synchronousQueue.take(); // 阻塞直到有生产者放入元素System.out.println("消费: " + message);System.out.println("消费完成"+i);Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}).start();// 启动生产者线程new Thread(() -> {for (int i = 1; i < 10; i++) {try {String message = "同步消息!"+i;System.out.println("生产: " + message);synchronousQueue.put(message); // 阻塞直到有消费者接收// synchronousQueue.put(message+"-new"); // 等前面阻塞了这句才会执行,即同一时刻队列中只有一个。System.out.println("生产完成"+i);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}}

5.7 LinkedTransferQueue 多对多同步

  • LinkedTransferQueue 支持高并发的生产者-消费者模型。提供了 TransferQueue 接口的实现,允许生产者等待消费者来消费元素。

  • LinkedTransferQueue 的 transfer 方法不管满不满都先阻塞,与 SynchronousQueue 不同的,他可以在多个生产者线程中使用,实现多个生产者等一个消费者。或者实现多个生产者等多个消费者。

    package top.yiqifu.study.p002_collections;import java.util.concurrent.LinkedTransferQueue;public class Test157_LinkedTransferQueue {public static void main(String[] args) {// 创建一个 LinkedTransferQueueLinkedTransferQueue<String> transferQueue = new LinkedTransferQueue<>();// 启动生产者线程for (int t = 1; t <= 3 ; t++) {new Thread(() -> {for (int i = 1; i <= 2; i++) {try {String producor = Thread.currentThread().getName();String message = "同步消息!" + i;System.out.println(producor+"生产: " + message);
    //                        transferQueue.put(message + " by put 1"); // 不阻塞
    //                        transferQueue.put(message + " by put 2");// 不阻塞
    //                        System.out.println(producor+"生产put完成");transferQueue.transfer(message + " by transfer");// 阻塞直到有消费者接收System.out.println(producor+"生产transfer完成" + i);} catch (InterruptedException e) {e.printStackTrace();}}}, "生产者"+t).start();}// 启动消费者线程new Thread(() -> {for (int i = 1; i < 20; i++) {try {System.out.println("消费准备"+i);String message = transferQueue.take(); // 阻塞直到有生产者放入元素System.out.println("消费: " + message);System.out.println("消费完成"+i);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}}

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

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

相关文章

J2EE(架构师考试复习资料)

J2EE 是针对 Web Service、业务对象、数据访问和消息报传送的一组规范。这组应用编程接口确定了 Web 应用与驻留它们的服务器之间的通信方式。J2EE 注重两件事&#xff0c;一是建立标准&#xff0c;使 Web 应用的部署与服务器无关&#xff1b;二是使服务器能控制构件的生命周期…

自定义Mybatis LanguageDriver性能优化

场景&#xff1a;高并发情况下mybatis 动态sql 解析 锁问题优化 优化前 并发测试 XMLLanguageDriver 类 的 createSqlSource 方法有锁 而且 每次执行时都会走该方法 优化前 &#xff1a; 线程有Block 优化后的 LanguageDriver public class CustomXMLLanguageDriver im…

大数据机器学习与深度学习——回归模型评估

大数据机器学习与深度学习——回归模型评估 回归模型的性能的评价指标主要有&#xff1a;MAE(平均绝对误差)、MSE(平均平方误差)、RMSE(平方根误差)、R2_score。但是当量纲不同时&#xff0c;RMSE、MAE、MSE难以衡量模型效果好坏&#xff0c;这就需要用到R2_score。 平均绝对…

Python实现多种图像锐化方法:拉普拉斯算子和Sobel算子

Python实现多种图像锐化方法&#xff1a;拉普拉斯算子和Sobel算子 图像和视频逐渐成为人们生活中信息获取的重要来源&#xff0c;而图像和视频在传输过程中有很多因素可能造成图像模糊&#xff0c;比如不正确的聚焦会产生离焦模糊&#xff0c;景物和照相机的相对运动会造成运动…

GBASE南大通用携手宇信科技打造“一表通”全链路解决方案

什么是“一表通”&#xff1f; “一表通”是国家金融监督管理总局为发挥统计监督效能、完善银行保险监管统计制度、推进监管数据标准化建设、打破数据壁垒&#xff0c;而制定的新型监管数据统计规范。相较于以往的报送接口&#xff0c;“一表通”提高了对报送时效性、校验准确性…

java集合的迭代器与遍历

文章目录 迭代器Iterator1、什么是Iterator2&#xff0c;iterator接口的API3、Irerator()方法细节解释4. Irerator的原理示意图5. forEach循环与Iterator遍历的区别与联系 ListIterator1.ListIterator的概述(1) 概念(2) 解析 2.ListIterator的生成3.ListIterator的API4.ListIte…

【从零开始学习JVM | 第九篇】了解 常见垃圾回收器

前言&#xff1a; 垃圾回收器&#xff08;Garbage Collector&#xff09;是现代编程语言中的一项重要技术&#xff0c;它提供了自动内存管理的机制&#xff0c;极大地简化了开发人员对内存分配和释放的繁琐工作。通过垃圾回收器&#xff0c;我们能够更高效地利用计算机的内存资…

selenium+python自动化测试 —— 解决无法启动IE浏览器及报错问题!

前言&#xff1a;记录启动IE浏览器的报错及解决方法。 错误1&#xff1a; selenium.common.exceptions.WebDriverException: Message: IEDriverServer.exe executable needs to be in PATH. Please download from http://selenium-release.storage.googleapis.com/index.html…

【C语言:动态内存管理】

文章目录 前言1.malloc2.free3.calloc4.realloc5.动态内存常见错误6.动态内存经典笔试题分析7.柔性数组8.C/C中的内存区域划分 前言 文章的标题是动态内存管理&#xff0c;那什么是动态内存管理&#xff1f;为什么有动态内存管理呢&#xff1f; 回顾一下以前学的知识&#xff…

SpringBoot+Vue3前后端快速整合入门

前言 最近需要维护一个个人项目&#xff0c;由于笔者是一个大后端&#xff0c;所以借此机会把前端学习过程记录一下&#xff0c;方便后续回顾。 前端项目初始化 安装npm 在前端项目初始化时&#xff0c;我们必须要安装好node&#xff0c;官网地址如下&#xff0c;因为笔者后…

1.【Multisim仿真】数电模电学习,仿真软件的初步使用

学习计划路径&#xff1a; >Multisim电路仿真软件熟练掌握 >数字电路基础课程 >逻辑电路设计与应用 >熟练掌握存储器、脉冲波形发生器、D/A和A/D转换器原理 >基本元器件熟练掌握 >晶体管放大电路及负反馈放大电路 >集成运算放大器设计 >电压变电流电路…

解决GateWay报错:Exceeded limit on max bytes to buffer : 262144

场景&#xff1a; 前端传来了一个大的字符串 发现请求不通 一番调试发现SpringGateway 默认内存缓冲区262144字节 网上查了很多种常见的解决方案无效之后 直接重写底层 网友的解决方案 方案1&#xff08;无效&#xff09; 直接修改缓冲区大小 spring:codec:max-in-memory-s…

【STM32】STM32学习笔记-LED闪烁 LED流水灯 蜂鸣器(06-2)

00. 目录 文章目录 00. 目录01. GPIO之LED电路图02. GPIO之LED接线图03. LED闪烁程序示例04. LED闪烁程序下载05. LED流水灯接线图06. LED流水灯程序示例07. 蜂鸣器接线图08. 蜂鸣器程序示例09. 下载10. 附录 01. GPIO之LED电路图 电路图示例1 电路图示例2 02. GPIO之LED接线图…

持续集成交付CICD:Jenkins使用GitLab共享库实现自动上传前后端项目Nexus制品

目录 一、实验 1.GitLab本地导入前后端项目 2.Jenkins新建前后端项目流水线 3.Sonarqube录入质量阈与质量配置 4.修改GitLab共享库代码 5.Jenkins手动构建前后端项目流水线 6.Nexus查看制品上传情况 7.优化代码获取RELEASE分支 8.优化Jenkins流水线项目名称 一、实验 …

计算机网络:数据链路层(网桥)

带你速通计算机网络期末 目录 一、冲突域和广播域 二、网桥介绍 三、网桥分类—―透明网桥 四、网桥分类―—源路由网桥 五、多接口网桥―—以太网交换机 总结 一、冲突域和广播域 冲突域:在同一个冲突域中的每一个节点都能收到所有被发送的帧。简单的说就是同一时间内只…

华为数通---配置基本QinQ示例

QinQ简介 定义 QinQ&#xff08;802.1Q-in-802.1Q&#xff09;技术是一项扩展VLAN空间的技术&#xff0c;通过在802.1Q标签报文的基础上再增加一层802.1Q的Tag来达到扩展VLAN空间的功能&#xff0c;可以使私网VLAN透传公网。由于在骨干网中传递的报文有两层802.1Q Tag&#x…

MySQL之DQL语句

DQL语句 DQL&#xff08;Data Query Language&#xff09;查询数据 操作查询&#xff1a;select简单的查询&#xff0c;复杂的查询数据库中最核心的语言&#xff0c;最重要的语句使用频繁的语句 指定查询 查询全部 语法&#xff1a; select 全部字段&#xff08;*&#x…

什么是tomcat?tomcat是干什么用的?

目录 Tomcat 的主要用途包括&#xff1a; 托管Java Web应用程序&#xff1a; Servlet 容器&#xff1a; 以下是关于Servlet容器的一些关键特性和功能&#xff1a; 生命周期管理&#xff1a; 多线程支持&#xff1a; HTTP请求处理&#xff1a; HTTP响应生成&#xff1a;…

金融众筹系统源码:适合创业孵化机构 附带完整的搭建教程

互联网技术的发展&#xff0c;金融众筹作为一种新型的融资方式&#xff0c;逐渐成为创业孵化机构的重要手段。为了满足这一需求&#xff0c;金融众筹系统源码就由此而生&#xff0c;并附带了完整的搭建教程。 以下是部分代码示例&#xff1a; 系统特色功能一览&#xff1a; 1.…

《从入门到精通:AJAX基础知识解析,前端开发中利器》基础篇

目录 学习目标&#xff1a; 学习目录&#xff1a; 学习时间&#xff1a; 学习内容&#xff1a; 什么是 AJAX&#xff1f; 怎么用 AJAX &#xff1f; 认识 URL 协议 域名 资源路径 获取 - 新闻列表 URL 查询参数 axios&#xff0d;查询参数 常用请求方法和数据提…