并发编程修炼七:同步容器

C++内功修炼:从内存管理到并发编程的实战进阶指南 在编程语言中,内存管理和并发编程是构建高性能、可靠系统的核心技术基础。理解内存模型、对象生命周期和指针机制,是掌握系统级编程的关键原理,这直接影响程序的稳定性和效率。通过RAII(资源获取即初始化)等设计模式,开发者可以自动化资源管理,避免内存泄漏和悬垂指针问题。在多线程环境下,互斥锁、条件变量和原子操作等技术确保了数据同步与线程安全,这对开发高并发应用具有重要价值。这些技术广泛应用于游戏引擎、高频交易系统和嵌入式开发等对性能要求严苛的场景。本文以C++为例,深入探讨智能指针、移动语义及现代并发工具的实际应 阅读详情

作者:海子
出处:http://www.cnblogs.com/dolphin0520/

目录:

一.为什么会出现同步容器?

二.Java中的同步容器类

三.同步容器的缺陷

四.自测试

一.为什么会出现同步容器?

在Java的集合容器框架中,主要有四大类别:List、Set、Queue、Map。

  List、Set、Queue接口分别继承了Collection接口,Map本身是一个接口。

  注意Collection和Map是一个顶层接口,而List、Set、Queue则继承了Collection接口,分别代表数组、集合和队列这三大类容器。

  像ArrayList、LinkedList都是实现了List接口,HashSet实现了Set接口,而Deque(双向队列,允许在队首、队尾进行入队和出队操作)继承了Queue接口,PriorityQueue实现了Queue接口。另外LinkedList(实际上是双向链表)实现了了Deque接口。

  像ArrayList、LinkedList、HashMap这些容器都是非线程安全的。

  如果有多个线程并发地访问这些容器时,就会出现问题。

  因此,在编写程序时,必须要求程序员手动地在任何访问到这些容器的地方进行同步处理,这样导致在使用这些容器的时候非常地不方便。

  所以,Java提供了同步容器供用户使用。

二.Java中的同步容器类

 在Java中,同步容器主要包括2类:

  1)Vector、Stack、HashTable

  2)Collections类中提供的静态工厂方法创建的类

  Vector实现了List接口,Vector实际上就是一个数组,和ArrayList类似,但是Vector中的方法都是synchronized方法,即进行了同步措施。

  Stack也是一个同步容器,它的方法也用synchronized进行了同步,它实际上是继承于Vector类。

  HashTable实现了Map接口,它和HashMap很相似,但是HashTable进行了同步处理,而HashMap没有。

  Collections类是一个工具提供类,注意,它和Collection不同,Collection是一个顶层的接口。在Collections类中提供了大量的方法,比如对集合或者容器进行排序、查找等操作。最重要的是,在它里面提供了几个静态工厂方法来创建同步容器类,如下图所示:
这里写图片描述

三.同步容器的缺陷

  从同步容器的具体实现源码可知,同步容器中的方法采用了synchronized进行了同步,那么很显然,这必然会影响到执行性能,另外,同步容器就一定是真正地完全线程安全吗?不一定,这个在下面会讲到。

  我们首先来看一下传统的非同步容器和同步容器的性能差异,我们以ArrayList和Vector为例:

1.性能问题

  我们先通过一个例子看一下Vector和ArrayList在插入数据时性能上的差异:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        ArrayList<Integer> list = new ArrayList<Integer>();
        Vector<Integer> vector = new Vector<Integer>();
        long start = System.currentTimeMillis();
        for(int i=0;i<100000;i++)
            list.add(i);
        long end = System.currentTimeMillis();
        System.out.println("ArrayList进行100000次插入操作耗时:"+(end-start)+"ms");
        start = System.currentTimeMillis();
        for(int i=0;i<100000;i++)
            vector.add(i);
        end = System.currentTimeMillis();
        System.out.println("Vector进行100000次插入操作耗时:"+(end-start)+"ms");
    }
}

 这段代码在我机器上跑出来的结果是:这里写图片描述
进行同样多的插入操作,Vector的耗时是ArrayList的两倍。

  这只是其中的一方面性能问题上的反映。

  另外,由于Vector中的add方法和get方法都进行了同步,因此,在有多个线程进行访问时,如果多个线程都只是进行读取操作,那么每个时刻就只能有一个线程进行读取,其他线程便只能等待,这些线程必须竞争同一把锁。

  因此为了解决同步容器的性能问题,在Java 1.5中提供了并发容器,位于java.util.concurrent目录下,并发容器的相关知识将在下一篇文章中讲述。

2.同步容器真的是安全的吗?

  也有有人认为Vector中的方法都进行了同步处理,那么一定就是线程安全的,事实上这可不一定。看下面这段代码:

public class Test {
    static Vector<Integer> vector = new Vector<Integer>();
    public static void main(String[] args) throws InterruptedException {
        while(true) {
            for(int i=0;i<10;i++)
                vector.add(i);
            Thread thread1 = new Thread(){
                public void run() {
                    for(int i=0;i<vector.size();i++)
                        vector.remove(i);
                };
            };
            Thread thread2 = new Thread(){
                public void run() {
                    for(int i=0;i<vector.size();i++)
                        vector.get(i);
                };
            };
            thread1.start();
            thread2.start();
            while(Thread.activeCount()>10)   {

            }
        }
    }
}

在我机器上运行的结果:

这里写图片描述

正如大家所看到的,这段代码报错了:数组下标越界。

  也许有朋友会问:Vector是线程安全的,为什么还会报这个错?很简单,对于Vector,虽然能保证每一个时刻只能有一个线程访问它,但是不排除这种可能:

  当某个线程在某个时刻执行这句时:

for(int i=0;i<vector.size();i++)
    vector.get(i);

  假若此时vector的size方法返回的是10,i的值为9

  然后另外一个线程执行了这句:

for(int i=0;i<vector.size();i++)
    vector.remove(i);

将下标为9的元素删除了。

  那么通过get方法访问下标为9的元素肯定就会出问题了。

  因此为了保证线程安全,必须在方法调用端做额外的同步措施,如下面所示:

public class Test {
    static Vector<Integer> vector = new Vector<Integer>();
    public static void main(String[] args) throws InterruptedException {
        while(true) {
            for(int i=0;i<10;i++)
                vector.add(i);
            Thread thread1 = new Thread(){
                public void run() {
                    synchronized (Test.class) {   //进行额外的同步
                        for(int i=0;i<vector.size();i++)
                            vector.remove(i);
                    }
                };
            };
            Thread thread2 = new Thread(){
                public void run() {
                    synchronized (Test.class) {
                        for(int i=0;i<vector.size();i++)
                            vector.get(i);
                    }
                };
            };
            thread1.start();
            thread2.start();
            while(Thread.activeCount()>10)   {

            }
        }
    }
}

个人认为

线程安全集合只能保证单个操作安全,复合操作同样不安全,同步容器中的所有自带方法都是线程安全的,因为方法都使用synchronized关键字标注。但是,对这些集合类的复合操作无法保证其线程安全性。需要客户端通过主动加锁来保证,就跟博主(海子)一样,在外面加关键字synchronized额外同步

查看jdk源码,会发现,像Vector这样的同步容器的所有共有方法全都是synchronized的。也就是说,我们可以在多线程场景中放心的使用单独这些方法,因为这些方法本身的确是线程安全的。那么为什么又说复合操作无法保证线程安全呢?这里举个栗子,我们定义如下删除Vector中最后一个元素方法:

public Object deleteLast(Vector v){
    int lastIndex  = v.size()-1;
    v.remove(lastIndex);
}

上面这个方法是一个复合方法,包括size()和remove(),乍一看上去好像并没有什么问题,无论是size()方法还是remove()方法都是线程安全的,那么整个deleteLast方法应该也是线程安全的。但是时,如果多线程调用该方法的过程中有,remove方法有可能抛出ArrayIndexOutOfBoundsException。我们看一下remove方法具体实现,什么情况下会抛出这个异常呢。

public synchronized E remove(int index) {
    modCount++;
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);
    E oldValue = elementData(index);

    int numMoved = elementCount - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--elementCount] = null; // Let gc do its work

    return oldValue;
}

从上面代码中可以看出,当index >= elementCount时,会抛出ArrayIndexOutOfBoundsException,也就是说,当当前索引值不再有效的时候,将会抛出这个异常。因为removeLast方法,有可能被多个线程同时执行,当线程一通过index()获得索引值为10,在尝试通过remove()删除该索引位置的元素之前,线程2把该索引位置的值删除掉了,这时线程一在执行时便会抛出异常。

为了避免出现类似问题,可以尝试加锁:

public void deleteLast() {
    synchronized (v) {
        int index = v.size() - 1;
        v.remove(index);
    }
}

如上,我们在deleteLast中,对v进行加锁,即可保证同一时刻,不会有其他线程删除掉v中的元素。

Ps:由于我们自己已知Vector等同步容器是线程安全的,所以我们通常在多线程场景中会直接拿来使用,并不会考虑太多,从而可能导致问题。
所以,我们在使用同步容器的时候,如果只使用其中的自带方法,那么可以放心使用,因为他们是线程安全的,但是如果我们想做复合操作,尤其是涉及到删除容器中的元素时,一定要注意是否需要客户端主动加锁。

下面,我们考虑以下代码,如果在多线程场景中使用会不会出现线程安全问题:

for (int i = 0; i < v.size(); i++) {
    System.out.println(v.get(i));
}

显然,以上代码在迭代的过程中,并不会出现线程安全问题。但是,如果在程序中还有以下代码有可能被同时调用呢?

for (int i = 0; i < v.size(); i++) {
    v.remove(i);
}

由于,不同线程在同一时间操作同一个Vector,其中包括删除操作,那么就同样有可能发生线程安全问题。所以,在使用同步容器的时候,如果涉及到多个线程同时执行删除操作,就要考虑下是否需要加锁。

3.ConcurrentModificationException异常

  在对Vector等容器并发地进行迭代修改时,会报ConcurrentModificationException异常,关于这个异常将会在后续文章中讲述。

  但是在并发容器中不会出现这个问题。

四.自测试(效率)

根据博主(海子)的代码测试

单线程:

public class Test {
    private static ArrayList<Integer> arraylist = new ArrayList<Integer>();
    private static Vector<Integer> vector = new Vector<Integer>();
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        for(int i=0;i<100000;i++)
            arraylist.add(i);
        long end = System.currentTimeMillis();
        System.out.println("ArrayList进行100000次插入操作耗时:"+(end-start)+"ms");
        start = System.currentTimeMillis();
        for(int i=0;i<100000;i++)
            vector.add(i);
        end = System.currentTimeMillis();
        System.out.println("Vector进行100000次插入操作耗时:"+(end-start)+"ms");


    }
}

效率:
当插入数据为:100000
这里写图片描述

当插入数据为:1000000
这里写图片描述
这里写图片描述

当插入数据为:10000000 (大数据插入反而是vector效率高)
这里写图片描述
这里写图片描述

结论和原因:
Vector的性能比ArrayList数据多的时候要好,单线程的话,上锁解锁的时间可以忽略,但是,arraylist和vector都是可变数组,当容量不够的时候,arraylist是增加自身的一半,而vector是增加一倍,从这里看vector要省很多时间在增加容量上。也许这就是优势所在。

多线程:

public class Test {
      private static ArrayList<Integer> list = new ArrayList<Integer>();
        private static Vector<Integer> vector = new Vector<Integer>();

        public static void main(String[] args) throws InterruptedException {

            for(int i=0;i<10;i++){
                Thread thread1 = new Thread(){
                    public void run(){
                        long start = System.currentTimeMillis();
                        for(int i=0;i<100000;i++)
                            list.add(i);
                        long end = System.currentTimeMillis();
                        System.out.println("ArrayList进行100000次插入操作耗时:"+(end-start)+"ms");
                    };
                };
                thread1.start();
            }

            for(int i=0;i<10;i++){
                Thread thread2 = new Thread(){
                    public void run(){
                        long start = System.currentTimeMillis();
                        for(int i=0;i<100000;i++)
                             vector.add(i);
                        long end = System.currentTimeMillis();
                        System.out.println("Vector进行100000次插入操作耗时:"+(end-start)+"ms");
                    };
                };
                thread2.start();
            }

        }
}

结果如图:
这里写图片描述

有Exception in thread “Thread-0” java.lang.ArrayIndexOutOfBoundsException异常,原因就在于ArrayList并不是线程安全的,它可能在拓展大小时,其他线程进行了add,所以会有这个异常出现。那还是要将ArrayList在synchronising环境下进行,那就没有可比性

高性能Java实战:从JVM调优到并发编程的完整知识体系 Java性能优化是每个开发者进阶的必经之路,其核心在于深入理解底层原理并掌握工程实践方法。从JVM内存模型与垃圾回收机制出发,理解对象生命周期与GC策略对应用停顿时间的影响,这是性能优化的基石。在并发编程领域,Java内存模型(JMM)与AQS框架为高并发设计提供了理论支撑,而LongAdder等工具则通过空间换时间的策略解决了超高并发计数场景的性能瓶颈。这些技术最终服务于构建高吞吐、低延迟的分布式系统,例如在电商、金融等实时性要求高的场景中,通过优化慢SQL查询、设计防缓存穿透方案,能有效提升系统稳定性。 阅读详情

相关推荐

透彻解析!mysql集群数据同步原理

Java面试指南 今天给大家分享一份【Java 面试 + Java 后端技术学习指南】:一份通向理想互联网公司的面试指南,包括 Java,技术面试必备基础知识、Leetcode、计算机操作系统、计算机网络、系统设计、分布式、数据库(MySQL、Redis)、Java 项目实战等, 新鲜出炉! 此手册内容专注 Java面试 ,这是本人花费了半年的时间总结的【Java 面试 + Java 后端技术学习指南】,目前本人已经拿到了腾讯等大厂offer,只要大家把这上面的知识点都搞明白,进入前 5 的互联网公司是绝对

m0_57066201的博客 553

高级工程师必备:技术深度与广度的修炼路径

在软件开发领域,技术深度与广度是衡量工程师能力的重要维度。从基础概念来看,技术深度涉及系统化思维、性能优化和架构设计等核心能力,而技术广度则包括基础设施掌握和领域驱动设计等。理解这些原理不仅能提升代码质量,还能有效管理技术债务,如在电商系统中通过多级缓存将QPS从2000提升到20000。这些技能在实际应用场景中尤为重要,比如秒杀系统优化和微服务拆分。通过掌握并发编程、JVM调优等硬核技能,工程师可以在高并发场景下实现系统性能质的飞跃。

weixin_33507732的博客 22

C++工业级开发九重修炼:从现代语法到系统架构实战

C++作为一门接近系统底层的编程语言,其核心价值在于提供零成本抽象和对硬件的直接掌控力,从而实现无与伦比的运行时性能。其原理建立在资源获取即初始化的RAII哲学之上,通过智能指针、移动语义等机制,将资源生命周期与对象生命周期绑定,确保了异常安全和内存安全。这种精细的控制能力,使其在需要极致性能与稳定性的技术领域具有不可替代的价值,广泛应用于游戏引擎、高频交易系统、嵌入式设备和大型分布式后台等场景。现代C++标准引入了模块、协程、概念等新特性,持续演进着开发范式,而像CMake这样的现代构建系统和vcpkg等

lvwenwen 475

Java核心基础:从JVM内存模型到并发编程的实战内功修炼

Java作为企业级应用的主流语言,其核心基础是构建稳健、高性能系统的基石。理解Java内存模型(JMM)是掌握并发编程的关键,它定义了线程间共享变量的访问规则,解决了可见性、原子性和有序性问题。通过深入分析JVM内存区域划分,如堆、方法区(Metaspace),开发者能够有效诊断和解决`OutOfMemoryError`等线上疑难杂症,这是性能调优与系统稳定的技术保障。在并发场景下,从`synchronized`锁的升级优化机制到`java.util.concurrent`工具包的无锁CAS实现,这些原理为

weixin_30399155的博客 395

两年经验的java程序员可以看哪些实用进阶的书籍?

不废话,直接推荐几本我读过的、自认不错的书。 1、网络编程书籍 网络编程,虽然有了 Netty 框架等很优质的网络框架了,但是理解背后的原理,对咱们定位项目的 bug,优化代码的性能,帮助非常大。 《Java TCP/IP Socket编程》(豆瓣 8.0 分) 这本书,我们既可以去学到必要的网络基础,也能学会怎么用 Java 去网络编程,非常推荐。 对于网络编程,大家都知道,这是易学难精的技能。学懂原理,了解网络编程,看看我上面推荐的书就够了。但是,要精通网络编程,写好网络相关的程序,那是相当不

Jsono8的博客 830

【Java多线程】JUC之并发容器—初识并发队列BlockingQueue(一)

- 一个是以`ConcurrentLinkedQueue`为代表的高性能`非阻塞队列`, - 一个是以`BlockingQueue接口`为代表的`阻塞队列`,**无论哪种顶级接口都继承自Queue**。 - **BlockingQueue是一个接口**,它的实现类有`ArrayBlockingQueue、LinkedBlockingDeque、PriorityBlockingQueue、DelayQueue、SynchronousQueue 、LinkedTransferQueue、LinkedBlock

墩墩分墩 2322

Java工程师修炼之道

sharding-jdbc:从数据源层面解决分库分表,读/写分离问题solr和ElasticSearch都是基于Lucene实现的搜索引擎,ElasticSearch对集群有良好的支持,逐渐成为主流Apache Kafka、RabbitMQ是使用比较普遍的消息队列,对消息丢失不敏感且不要求消息事务的,选择Kafka,否则选择RabbitMQ分布式文件存储:HDFS、NFS、Samba主流的RPC协议:Dubbo、Hession、RMI等分布式调度(定时器):elastic-job。

m0_46612024的博客 305

【Java多线程】JUC之并发容器—深入剖析并发队列SynchronousQueue(七)

- **SynchronousQueue**: 一个 **不存储元素(没有容量)** 的阻塞队列,,`每个插入操作(put)必须等到另一个线程调用移除操作(take)` (`即: 写入元素必须被移除后才能继续写入新的元素`),否则写入操作一直处于阻塞状态。支持公平锁(TransferQueue-FIFO)和非公平锁(TransferStack-LIFO)。相较于其他可缓存元素队列,适用于`单线程同步传递性场景`,**比如:消费者没拿走当前的产品,生产者是不能再给产品的,这样可以`控制生产者生产的速率和消费者

墩墩分墩 2734

《程序员修炼之道》第五六章读书笔记

《程序员修炼之道》读书笔记

qq_45774406的博客 478

【Java多线程】JUC之并发容器—深入剖析并发队列ConcurrentLinkedDeque(十)

**ConcurrentLinkedDeque**: 基于`双向链表`实现的`无界非阻塞线程安全队列`,可以从队列的头和尾同时操作(插入/删除),底层基于`自旋+CAS`的方式实现,**适合“`多生产,多消费`”的场景。** - 双端队列与普通队列的入队区别是:双端队列中元素可以在`“队首、队尾进行入队、出队”` - “**队首入队**”底层调用了`linkFirst()`方法,而“**队尾入队**”是调用了` linkLast()`方法 - “**队首出队**”底层调用了`unlinkFirst(

墩墩分墩 3669

【Java多线程】JUC之并发容器—深入剖析并发队列ConcurrentLinkedQueue(九)

**ConcurrentLinkedQueue**:是一个适用于**高并发场景**,基于`单向链表`实现的`无界非阻塞线程安全队列`,使用`CAS自旋+volatile`来包保证线程安全。按照`FIFO`的方式对存储元素进行排序,该队列`不允许写入null元素`。 - 由于是完全基于`无锁算法`实现的,多线程同时进行修改队列的操作(比如同时入队),很可能出现`CAS修改失败`的情况,那么失败的线程`会进入下一次自旋,再尝试入队操作,直到成功`。**所以,在并发量适中的情况下,ConcurrentLinked

墩墩分墩 2595

古法编程:程序员必备的底层原理与系统思维修炼指南

在软件开发领域,理解计算机系统原理是构建高性能、高可靠性应用的基础。从内存管理、CPU缓存到操作系统进程调度,这些底层机制决定了程序的执行效率与资源利用。掌握这些核心原理,不仅能提升深度调试与性能优化的能力,更能帮助开发者正确理解并高效运用现代抽象技术,如容器化与并发框架。在数据库查询优化、内存泄漏排查等常见工程实践中,系统思维能引导开发者从现象直抵本质。本文聚焦于古法编程的价值,探讨如何通过C语言实践、操作系统核心概念学习及工具链掌握,系统性地构建可迁移的解决问题能力,从而在快速迭代的技术浪潮中保持核心竞

weixin_33908217的博客 350

Java并发编程78讲:从JMM到线程池的系统学习路线

并发编程是Java后端开发绕不开的核心能力,尤其在高流量场景下,线程安全与性能优化直接决定系统稳定性。理解Java内存模型(JMM)与原子性、可见性、有序性三大特性,是掌握锁机制的前提。从synchronized的锁升级到AQS队列同步器,再到ConcurrentHashMap、线程池等JUC组件,构成了并发工具箱的完整图谱。这些原理广泛应用于秒杀扣减库存、异步任务处理、分布式缓存一致性等实际业务;与此同时,死锁排查、ThreadLocal内存泄漏、CAS的ABA问题也是面试高频考点。本文基于《Java并发

weixin_33507732的博客 260

闭关修炼。。这套Java面试题我要死记硬背

作者:IT--小艾 链接:闭关修炼!这套Java面试题我要死记硬背!_笔经面经_牛客网 来源:牛客网 Java 后端开发面试总结包含了 JavaOOP、Java 集合容器、Java 异常、并发编程、Java 反射、Java 序列化、JVM、Redis、Spring MVC、MyBatis、MySQL 数据库、消息中间件 MQ、Dubbo、Linux、ZooKeeper、 分布式 &数据结构与算法等 25 个专题技术点 JavaOOP 面试题 Java 集合/泛型面试题 Java 异常面试...

m0_67322837的博客 216

Truncated SVD for faster dection

Truncated SVD方法最先被提出在《Fast R-CNN》论文中,用于降低全连接层的运算量,提升模型的速度。在卷积神经网络的分类任务中,FC层的运算速度远远大于CONV层,而在Fast R-CNN的目标检测任务中,大多数的运算时间发生在FC 的运算中,是因为针对每张输入图片,只进行一次卷积操作,而要对生成的Region Proposal,大致两千个,进行全连接操作进行分类与Bounding...

weixin_35747666的博客 1535

【毕设课设】基于YOLO v3与LSTM的水位智能识别系统(附完整源码+数据集)

本文详细介绍了基于YOLO v3与LSTM的水位智能识别系统的完整实现方案。该系统通过YOLO v3精准定位水位尺数字,结合LSTM-OCR进行序列识别,并融合霍夫变换检测刻度线,实现高精度水位自动读取。项目提供了从环境搭建、模型训练到集成部署的全流程指南,并附有完整源码和数据集,是理想的毕设课设实战项目。

php55的博客 609

python并发编程_【2020Python修炼记】python并发编程(一)多进程|python基础教程|python入门|python教程...

https://www.xin3721.com/eschool/pythonxin3721/一、操作系统发展史1、第一代计算机(1940~1955):真空管和穿孔卡片(1)特点: 没有操作系统的概念 所有的程序设计都是直接操控硬件(2)优点:程序员在申请的时间段内独享整个资源,可以即时地调试自己的程序(有bug可以立刻处理)(3)缺点:浪费计算机资源,一个时间段内只有一个人用。 注意:同一时刻只有...

weixin_39940770的博客 186

Microchip Studio 7.0 从零开始:下载、安装到首个AVR工程实战

本文为AVR单片机开发新手提供了Microchip Studio 7.0的完整入门指南。从官方下载、避坑安装,到创建首个GCC C工程、编写LED闪烁代码,再到项目配置、仿真调试与程序烧录,手把手带你完成从零到一的实战过程,是掌握AVR开发环境的必备教程。

weixin_29200741的博客 522

python如何并发运行2个软件_【2020Python修炼记】python并发编程(二)多进程-理论部分...

【目录】一、什么是进程二、进程与程序的区别三、进程的并行与并发四、多道技术五、同步 、异步 / 阻塞、非阻塞的概念一、什么是进程(Process)# 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。简单地说,进程 就是 正在进行的一个过程或者说一个任务。而负责执行任务则是cpu。在早期面向进程设计的计算机结构中,进程是...

weixin_39907289的博客 275

跨模型的 API 调用与集成:使用 Spring AI 构建可移植的 API 代码,支持多个 AI 模型(如 OpenAI、Anthropic 等)的调用

Spring AI 是 Spring 生态系统中的一部分,旨在提供一种简单、可扩展的方式来集成人工智能模型。Spring AI 使得开发者可以使用现有的 Spring 体系结构方便地访问多种 AI 模型和服务,例如 OpenAI、Anthropic、HuggingFace 等。

秋元的博客 2404

不用标注数据!用SAM 3零成本搞定遥感图像分割:SegEarth-OV3保姆级上手教程

本文介绍了如何利用SAM 3和SegEarth-OV3实现零标注遥感图像分割,提供从环境配置到结果优化的完整教程。通过开放词汇语义分割技术,用户无需标注数据即可快速获得专业级分割结果,适用于光伏板统计、灾害评估等场景。

weixin_30415113的博客 586

从容器适配器到线程安全:C++栈与队列的深度实现与设计哲学

容器适配器是C++标准库中一种重要的设计模式,它通过组合而非继承的方式,复用底层容器的功能来实现特定的数据结构接口。其核心原理在于将数据存储与访问逻辑分离,底层容器负责内存管理和元素存储,适配器则定义特定的访问规则(如LIFO或FIFO)。这种设计极大地提升了代码的复用性和灵活性,是构建高效、可维护基础组件的关键技术。在工程实践中,容器适配器不仅保证了数据结构的核心语义(例如通过限制迭代器来维护栈和队列的行为约束),也为性能优化(如动态扩容策略)和功能扩展(如集成自定义分配器)提供了清晰框架。其应用场景广泛

weixin_33882452的博客 353

西门子S7-1200和IFM IO-Link主站连接与设定

硬件配置:S7-1200 1214DC/DC/DC,IFM AL1102支持Profinet,IFM支持IO-Link的流量传感器SBG233,流量开关SI5010,接近开关IF6123。第三步:打开IFM moneo configure可查看到流量传感器SBG233 PLC映射所有输出共占用4个byte,也可在IFM官网下载传感器对应IODD文件。第五步:组态编程编译下载。最近新做了一个项目,用到了IO-Link设备,今天来分享一下S7-1200和IFM IO-Link主站模块AL1102的连接过程。

GreatShen168的博客 6492

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 289

[DDR5] 模式寄存器 Mode Register MRR/MRW

模式寄存器用于定义各种操作模式。在初始化过程中,可以通过重新执行MRS命令来更改模式寄存器的内容。

串起点滴 1639

Deepseek Engram 论文全面解读

以下是对 DeepSeek 与北京大学于联合发布的论文《》(简称Engram 论文)的。

lpfasd123的博客 355
上一篇: 并发编程修炼六:volatile关键字解析
下一篇: 并发编程修炼八:Java ConcurrentModificationException异常原因和解决方法
yjgithub
博客等级 码龄11年 53粉丝 105原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值