15.JUC原子类

文章目录

  • JUC原子类
    • 1.JUC中的Atomic原子操作包
      • 1.1. 基本原子类(Basic Atomic Classes)
      • 1.2. 数组原子类(Array Atomic Classes)
      • 1.3. 引用原子类(Reference Atomic Classes)
      • 4. 字段更新原子类(Field Updater Atomic Classes)
    • 2.基础原子类(Atomic Integer)
      • 2.1.基本方法
      • 2.2.案例代码
    • 3.数组原子类
      • 3.1.基本方法
      • 3.2.案例代码
    • 4.AtomicInteger线程安全原理
    • 5.对象操作原子性
      • 5.1引用类型原子类
      • 5.2.案例代码
    • 6.属性更新原子类
      • 6.1.属性更新原子类
      • 6.2.案例代码

JUC原子类

在多线程并发执行过程中,例如++ ,–,这类运算符都是不具备原子性的,通常我们会使用synchronized将其变为同步操作,但是这样会导致性能上的下降。JDK为这些不安全的操作提供了一些原子类,JDK原子类基于CAS轻量级原子操作实现,使得运行效率变得很高。

1.JUC中的Atomic原子操作包

Java中的java.util.concurrent.atomic包提供了一系列的原子操作类,用于在多线程环境下进行原子操作。这些类通常用于实现线程安全的计数器、标志位、引用等,并且比使用synchronized关键字或者volatile变量实现的方式更高效。

在这里插入图片描述

根据操作目标的数据类型,可以将JUC包的原子类分为以下4类,基本原子类数组原子类引用原子类字段更新原子类

1.1. 基本原子类(Basic Atomic Classes)

这些类用于对基本数据类型进行原子操作,如int、long、boolean等。

  • AtomicBoolean:提供了对boolean类型值的原子更新操作,使用CAS算法来保证原子性。

    AtomicBoolean atomicBoolean = new AtomicBoolean(true);
    atomicBoolean.getAndSet(false); // 设置新值并返回旧值
    
  • AtomicInteger:提供了对int类型值的原子更新操作,同样使用CAS算法。

    AtomicInteger atomicInteger = new AtomicInteger(0);
    atomicInteger.incrementAndGet(); // 原子地将当前值加一,并返回增加后的值
    
  • AtomicLong:提供了对long类型值的原子更新操作。

    AtomicLong atomicLong = new AtomicLong(0L);
    atomicLong.compareAndExchange(0L, 1L); // 如果当前值等于预期值,则更新为新值
    

1.2. 数组原子类(Array Atomic Classes)

这些类用于对数组中的元素进行原子操作。

  • AtomicIntegerArray:提供了对int数组中元素的原子更新操作。

    AtomicIntegerArray atomicIntArray = new AtomicIntegerArray(5);
    atomicIntArray.getAndIncrement(0); // 原子地将指定索引位置的元素加一,并返回原值
    
  • AtomicLongArray:提供了对long数组中元素的原子更新操作。

    AtomicLongArray atomicLongArray = new AtomicLongArray(5);
    atomicLongArray.addAndGet(0, 10L); // 原子地将指定索引位置的元素加上给定的值,并返回增加后的值
    
  • AtomicReferenceArray:提供了对引用类型数组中元素的原子更新操作。

    AtomicReferenceArray<String> atomicRefArray = new AtomicReferenceArray<>(new String[]{"a", "b", "c"});
    atomicRefArray.compareAndSet(1, "b", "new_value"); // 如果当前值等于预期值,则更新为新值
    

1.3. 引用原子类(Reference Atomic Classes)

这些类用于对引用类型数据进行原子操作。

  • AtomicReference:提供了对引用类型值的原子更新操作。

    AtomicReference<String> atomicReference = new AtomicReference<>("initial_value");
    atomicReference.getAndSet("new_value"); // 设置新值并返回旧值
    

4. 字段更新原子类(Field Updater Atomic Classes)

这些类用于对指定类的指定字段进行原子更新操作,通常用于性能优化。

  • AtomicIntegerFieldUpdater:用于对指定类的指定volatile int字段进行原子更新操作。

    AtomicIntegerFieldUpdater<MyClass> updater = AtomicIntegerFieldUpdater.newUpdater(MyClass.class, "fieldName");
    updater.incrementAndGet(instance); // 原子地将指定实例的字段值加一,并返回增加后的值
    
  • AtomicLongFieldUpdater:用于对指定类的指定volatile long字段进行原子更新操作。

    AtomicLongFieldUpdater<MyClass> updater = AtomicLongFieldUpdater.newUpdater(MyClass.class, "fieldName");
    updater.compareAndSet(instance, 0L, 1L); // 如果当前值等于预期值,则更新为新值
    
  • AtomicReferenceFieldUpdater:用于对指定类的指定volatile引用字段进行原子更新操作。

    AtomicReferenceFieldUpdater<MyClass, String> updater = AtomicReferenceFieldUpdater.newUpdater(MyClass.class, String.class, "fieldName");
    updater.getAndSet(instance, "new_value"); // 设置新值并返回旧值
    

这些原子类提供了一系列的原子操作方法,可以确保在多线程环境下的操作是线程安全的,避免了使用synchronized关键字或者volatile变量所带来的性能开销。

2.基础原子类(Atomic Integer)

2.1.基本方法

  1. get(): 获取当前的值。
  2. set(int newValue): 设置为指定的值。
  3. getAndSet(int newValue): 设置为指定的值,并返回旧值。
  4. incrementAndGet(): 将当前值加 1,并返回增加后的值。
  5. decrementAndGet(): 将当前值减 1,并返回减少后的值。
  6. getAndIncrement(): 返回当前值,并将其加 1。
  7. getAndDecrement(): 返回当前值,并将其减 1。
  8. compareAndSet(int expect, int update): 如果当前值等于期望值,则设置为新值,并返回 true,否则返回 false。

2.2.案例代码

通过调用Atomict提供的API 实现了原子性 修改数据

@Test
@DisplayName("测试AtomicInteger")
public void testAtomicInteger() {log.info("当前值: {}", counter.get());counter.set(10);log.info("设置值为 10");int oldValue = counter.getAndSet(20);log.info("旧值: {}, 新值: {}", oldValue, counter.get());log.info("增加后的值: {}", counter.incrementAndGet());log.info("减少后的值: {}", counter.decrementAndGet());oldValue = counter.getAndIncrement();log.info("旧值: {}, 新值: {}", oldValue, counter.get());oldValue = counter.getAndDecrement();log.info("旧值: {}, 新值: {}", oldValue, counter.get());boolean success = counter.compareAndSet(18, 30);log.info("CAS 结果: {}, 当前值: {}", success, counter.get());}

在这里插入图片描述

上面的操作是在单线程的环境下,下面我通过多线程来实现 数据的一个自增操作

@Test
@DisplayName("多线程实现自增运算")
public void test() throws InterruptedException {ArrayList<Thread> threads = new ArrayList<>();CountDownLatch latch = new CountDownLatch(10);for (int i = 0; i < 10; i++) {threads.add(new Thread(()->{// 每个线程进行 100次自增运算for (int j = 0; j < 100; j++) {counter.incrementAndGet();}latch.countDown();}));}// 全部启动线程threads.forEach(Thread::start);// 等待全部线程执行结束latch.await();//打印最终结果log.error("10个线程执行自增后的结果为:{}", counter.get());
}

在这里插入图片描述

3.数组原子类

3.1.基本方法

  1. get(int index): 获取指定索引处的值。
  2. set(int index, int newValue): 设置指定索引处的值。
  3. getAndSet(int index, int newValue): 设置指定索引处的值,并返回旧值。
  4. incrementAndGet(int index): 将指定索引处的值加 1,并返回增加后的值。
  5. decrementAndGet(int index): 将指定索引处的值减 1,并返回减少后的值。
  6. addAndGet(int index, int delta): 将指定索引处的值加上给定的增量,并返回增加后的值。
  7. compareAndSet(int index, int expect, int update): 如果指定索引处的值等于期望值,则设置为新值,

3.2.案例代码

// 数组原子类
private static final AtomicIntegerArray array = new AtomicIntegerArray(5);@Test
@DisplayName("测试数组原子类")
public void testAtomicIntegerArray() {// 1. get(int index)log.info("当前数组值: {}", array.toString());// 2. get(int index)log.info("索引为 0 的值: {}", array.get(0));// 3. set(int index, int newValue)array.set(1, 10);log.info("将索引为 1 的值设置为 10");// 4. getAndSet(int index, int newValue)int oldValue = array.getAndSet(2, 20);log.info("索引为 2 的旧值: {}, 新值: {}", oldValue, array.get(2));// 5. incrementAndGet(int index)log.info("索引为 0 的值自增后的值: {}", array.incrementAndGet(0));// 6. decrementAndGet(int index)log.info("索引为 1 的值自减后的值: {}", array.decrementAndGet(1));// 7. addAndGet(int index, int delta)log.info("索引为 2 的值加上 5 后的值: {}", array.addAndGet(2, 5));// 8. compareAndSet(int index, int expect, int update)boolean success = array.compareAndSet(3, 18, 30);log.info("CAS 结果: {}, 当前值: {}", success, array.get(3));
}

运行以上程序,结果如下:

在这里插入图片描述

4.AtomicInteger线程安全原理

基础原子类(以AtomicInteger为例),主要是通过CAS自旋 + volatile相结合的方案实现,既保证了变量操作线程的原子性,有避免了synchronized重量级锁的开销,使得Java程序的效率得到大幅度提升。

下面简单 以AtomicInteger源码 分析一下 原子类的CAS自旋 + volatile相结合的方案(JDK 17发生 了变化)

public class AtomicInteger extends Number implements java.io.Serializable {private static final long serialVersionUID = 6214790243416807050L;// 使用Unsafe类获取Unsafe实例,用于执行CAS操作private static final Unsafe U = Unsafe.getUnsafe();// VALUE字段的偏移量private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");// 使用volatile关键字保证多线程间的可见性private volatile int value;// 构造方法,初始化AtomicInteger的值public AtomicInteger(int initialValue) {value = initialValue;}// 构造方法,默认初始值为0public AtomicInteger() {}// 获取当前值public final int get() {return value;}// 设置新值public final void set(int newValue) {value = newValue;}// 延迟设置新值,使用setRelease方法public final void lazySet(int newValue) {U.putIntRelease(this, VALUE, newValue);}// 获取并设置新值,返回旧值public final int getAndSet(int newValue) {return U.getAndSetInt(this, VALUE, newValue);}// 比较并设置新值,如果当前值等于期望值,则设置为新值,返回设置前的值public final boolean compareAndSet(int expectedValue, int newValue) {return U.compareAndSetInt(this, VALUE, expectedValue, newValue);}// 下面的方法实现了自增、自减、加法运算,并返回相应的结果,以及更新当前值的功能,使用CAS操作保证原子性// 自增并获取当前值public final int getAndIncrement() {return U.getAndAddInt(this, VALUE, 1);}// 自减并获取当前值public final int getAndDecrement() {return U.getAndAddInt(this, VALUE, -1);}// 加上指定的值并获取当前值public final int getAndAdd(int delta) {return U.getAndAddInt(this, VALUE, delta);}// 自增并获取更新后的值public final int incrementAndGet() {return U.getAndAddInt(this, VALUE, 1) + 1;}// 自减并获取更新后的值public final int decrementAndGet() {return U.getAndAddInt(this, VALUE, -1) - 1;}// 加上指定的值并获取更新后的值public final int addAndGet(int delta) {return U.getAndAddInt(this, VALUE, delta) + delta;}// 下面的方法为类型转换方法,用于转换为其他基本数据类型// 返回int值public final String toString() {return Integer.toString(get());}// 返回当前值的int表示public int intValue() {return get();}// 返回当前值的long表示public long longValue() {return (long)get();}// 返回当前值的float表示public float floatValue() {return (float)get();}// 返回当前值的double表示public double doubleValue() {return (double)get();}// JDK 9之后的新增方法,使用plain、opaque、acquire、release等方法增加了对内存操作的控制// 返回当前值,使用非volatile内存语义public final int getPlain() {return U.getInt(this, VALUE);}// 设置新值,使用非volatile内存语义public final void setPlain(int newValue) {U.putInt(this, VALUE, newValue);}// 返回当前值,使用opaque内存语义public final int getOpaque() {return U.getIntOpaque(this, VALUE);}// 设置新值,使用opaque内存语义public final void setOpaque(int newValue) {U.putIntOpaque(this, VALUE, newValue);}// 返回当前值,使用acquire内存语义public final int getAcquire() {return U.getIntAcquire(this, VALUE);}// 设置新值,使用release内存语义public final void setRelease(int newValue) {U.putIntRelease(this, VALUE, newValue);}// 使用compareAndExchange方法实现CAS操作,设置新值,返回旧值public final int compareAndExchange(int expectedValue, int newValue) {return U.compareAndExchangeInt(this, VALUE, expectedValue, newValue);}// 使用compareAndExchangeAcquire方法实现CAS操作,设置新值,返回旧值public final int compareAndExchangeAcquire(int expectedValue, int newValue) {return U.compareAndExchangeIntAcquire(this, VALUE, expectedValue, newValue);}// 使用compareAndExchangeRelease方法实现CAS操作,设置新值,返回旧值public final int compareAndExchangeRelease(int expectedValue, int newValue) {return U.compareAndExchangeIntRelease(this, VALUE, expectedValue, newValue);}// 使用weakCompareAndSetInt方法实现CAS操作,设置新值,返回操作是否成功public final boolean weakCompareAndSetVolatile(int expectedValue, int newValue) {return U.weakCompareAndSetInt(this, VALUE, expectedValue, newValue);}// 使用weakCompareAndSetIntAcquire方法实现CAS操作,设置新值,返回操作是否成功public final boolean weakCompareAndSetAcquire(int expectedValue, int newValue) {return U.weakCompareAndSetIntAcquire(this, VALUE, expectedValue, newValue);}// 使用weakCompareAndSetIntRelease方法实现CAS操作,设置新值,返回操作是否成功public final boolean weakCompareAndSetRelease(int expectedValue, int newValue) {return U.weakCompareAndSetIntRelease(this, VALUE, expectedValue, newValue);}
}// 其中自旋操作在UnSafe中实现 /*** Atomically adds the given value to the current value of a field* or array element within the given object {@code o}* at the given {@code offset}.** @param o object/array to update the field/element in* @param offset field/element offset* @param delta the value to add* @return the previous value* @since 1.8*/@IntrinsicCandidatepublic final int getAndAddInt(Object o, long offset, int delta) {int v;do {v = getIntVolatile(o, offset);} while (!weakCompareAndSetInt(o, offset, v, v + delta));return v;}@IntrinsicCandidatepublic native int     getIntVolatile(Object o, long offset);// 比较期望值 并设置@IntrinsicCandidatepublic final boolean weakCompareAndSetInt(Object o, long offset,int expected,int x) {return compareAndSetInt(o, offset, expected, x);}

5.对象操作原子性

基础原子类型 只能保证一个变量的原子操作,但是当需要对多个变量操作时,CAS无法保证原子性操作,这个时候可以使用AtomicReference(原子引用类型),保证对象引用的原子性。

简单来说,如果需要同时保证多个变量操作原子性,可以把多个变量放入一个对象中进行操作

5.1引用类型原子类

  • AtomicReference :基础引用原子类

    • get():获取当前存储在AtomicReference中的对象。
    • set():设置AtomicReference中的对象为指定值。
    • compareAndSet():如果当前存储在AtomicReference中的对象等于预期值,则将AtomicReference中的对象设置为新值。
    • getAndSet():获取当前存储在AtomicReference中的对象,并设置AtomicReference中的对象为新值。
  • AtomicStampedReference:原子更新带有整数标记的引用。此类可以用于解决CAS操作的ABA问题。ABA问题是指在使用CAS进行原子更新时,如果被更新的数据从A变成了B,然后又变回了A,这样的状态变化可能会导致误判。AtomicStampedReference通过引入版本号(或时间戳)来解决这个问题,它将每次更新都与一个标记相关联,以便在比较时不仅考虑引用对象的值,还要考虑其标记。

    主要方法:

    • boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp):如果当前引用和标记都与预期值匹配,则原子地将引用和标记的值设置为新值。
  • AtomicMarkableReference:原子更新带有标记位的引用。与AtomicStampedReference类似,但是标记是一个布尔值,只有两种状态(true或false)。这个类通常用于简单的标记,而不需要额外的版本信息。

    主要方法:

    • boolean compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark):如果当前引用和标记都与预期值匹配,则原子地将引用和标记的值设置为新值。

下面主要介绍一下AtomicReference

5.2.案例代码

    @Test@DisplayName("测试基础引用原子类型")public void test2() {// 创建一个初始值为null的AtomicReference对象AtomicReference<Student> atomicReference = new AtomicReference<>(null);Student student = new Student("张三",21);// 设置AtomicReference中的对象值为atomicReference.set(student);log.error("当前AtomicReference中的对象值为: " + atomicReference.get());// 比较并设置操作,如果当前对象值等于"student",则设置为"新的student"boolean success = atomicReference.compareAndSet(student,new Student("李四",21));if (success) {log.error("对象值成功被修改为: " + atomicReference.get());} else {log.error("对象值未被修改,当前值为: " + atomicReference.get());}// 获取并设置操作,获取当前对象值并设置为"王五 32"Student studentOld = atomicReference.getAndSet(new Student("王五", 32));log.error("获取到的旧对象值为: " + studentOld);log.error("当前AtomicReference中的对象值为: " + atomicReference.get());}@Getter@Setter@ToStringclass Student{private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}}

在这里插入图片描述

注意:使用原子应用类型AtomicReference操作包装了Student,只能保证Student引用的原子的操作,对被包装的Student对象的字段修改时并不能保证原子性(说白了,student.setName() 这种是不具备原子性的 new Student(“xxx”,232) 这种 通过包装统一更新的 是具备原子性的)

6.属性更新原子类

属性更新原子类是Java并发包提供的一组类,用于原子性地更新对象中的属性值。这些类通常用于保证多线程环境下的线程安全性,避免竞态条件和数据不一致性问题。

6.1.属性更新原子类

属性类更新原子类有以下三个

  1. AtomicIntegerFieldUpdater:原子更新整型字段的值。
    1. static <T> AtomicIntegerFieldUpdater<T> newUpdater(Class<T> tClass, String fieldName):创建一个新的原子整型字段更新器。
    2. int get(T obj):获取指定对象中字段的当前值。
    3. void set(T obj, int newValue):设置指定对象中字段的值为指定的新值。
    4. int getAndSet(T obj, int newValue):获取并设置指定对象中字段的值为指定的新值。
  2. AtomicLongFieldUpdater:原子更新长整型字段的值。
    1. static <T> AtomicLongFieldUpdater<T> newUpdater(Class<T> tClass, String fieldName):创建一个新的原子长整型字段更新器。
    2. long get(T obj):获取指定对象中字段的当前值。
    3. void set(T obj, long newValue):设置指定对象中字段的值为指定的新值。
    4. long getAndSet(T obj, long newValue):获取并设置指定对象中字段的值为指定的新值。
  3. AtomicReferenceFieldUpdater<T, V>:原子更新引用类型字段的值。
    1. static <T,V> AtomicReferenceFieldUpdater<T,V> newUpdater(Class<T> tClass, Class<V> vClass, String fieldName):创建一个新的原子引用类型字段更新器。
    2. V get(T obj):获取指定对象中字段的当前值。
    3. void set(T obj, V newValue):设置指定对象中字段的值为指定的新值。
    4. V getAndSet(T obj, V newValue):获取并设置指定对象中字段的值为指定的新值。

使用属性更新原子类保障安全性的主要流程 大致分为两部

  1. 更新的对象的属性必须是 public volatile修饰符
  2. 因为对象的属性修改类型原子类都是抽象类,所以每次每次使用都必须使用静态方法

6.2.案例代码

    @Test@DisplayName("属性更新原子类")public void test3() {AtomicIntegerFieldUpdater<Data> updater = AtomicIntegerFieldUpdater.newUpdater(Data.class, "value");Data data = new Data(10);// 获取并输出当前值log.error("初始值:{}",updater.get(data));// 尝试原子性地将值更新为20updater.set(data, 20);log.error("更新后的值:{}",updater.get(data));// 尝试原子性地获取并更新值为30int oldValue = updater.getAndSet(data, 30);log.error("原来的值:{}", oldValue);log.error("更新后的值:{}", updater.get(data));}class Data {public volatile int value; // 要更新的字段必须是 volatile 的public Data(int value) {this.value = value;}}

在这里插入图片描述

这个代码了如何使用AtomicIntegerFieldUpdater类来原子性地更新Data对象中的整型字段value的值。

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

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

相关文章

StackQueue+泛型简单理解

&#x1f341; 个人主页&#xff1a;爱编程的Tom&#x1f4ab; 本篇博文收录专栏&#xff1a;Java专栏&#x1f449; 目前其它专栏&#xff1a;c系列小游戏 c语言系列--万物的开始_ &#x1f389; 欢迎 &#x1f44d;点赞✍评论⭐收藏&#x1f496;三连支持一…

LeetCode2215找出两数组的不同

题目描述 给你两个下标从 0 开始的整数数组 nums1 和 nums2 &#xff0c;请你返回一个长度为 2 的列表 answer &#xff0c;其中&#xff1a;answer[0] 是 nums1 中所有 不 存在于 nums2 中的 不同 整数组成的列表。answer[1] 是 nums2 中所有 不 存在于 nums1 中的 不同 整数组…

Autosar架构

蓝框那种叫component&#xff0c;绿框的叫function cluster。 接口 有三种接口&#xff0c;RTE跟SWC之间链接的叫Autosar Interface&#xff0c;RTE跟BSW的Components链接是Standardized Interface&#xff0c;RTE跟BSW的services链接的是Standardized Autosar Interface。 St…

网络协议的分类

1.概要 网络协议可以分为三类&#xff1a; 封装协议路由协议功能类协议 2.分类说明 OSPF报文直接调用_ IP协议__协议进行封装&#xff0c;以目的地址_244.0.0.5 __发送到所有的OSPF路由器? 244.0.0.1 所有主机&#xff1b;244.0.0.2 所有路由器&#xff1b;244.0.0.6 指定…

Vue+springboot的批量删除功能

vue前台 <div style"margin-bottom: 10px"><el-button type"primary" plain click"handleAdd">新增</el-button><el-button click"delBatch" type"danger" plain style"margin-left: 5px"…

C语言例题41、八进制转换为十进制

#include<stdio.h>void main() {int x;printf("请输入一个8进制整数&#xff1a;");scanf("%o", &x);printf("转换成十进制后的整数为%d\n", x); }运行结果&#xff1a; 本章C语言经典例题合集&#xff1a;http://t.csdnimg.cn/FK0Qg…

Java入门基础学习笔记21——Scanner

在程序中接收用户通过键盘输入的数据&#xff1a; 需求&#xff1a; 请在程序中&#xff0c;提示用户通过键盘输入自己的姓名、年龄、并能在程序中收到这些信息&#xff0c;怎么解决&#xff1f; Java已经写好了实现程序&#xff0c;我们调用即可。 API&#xff1a;Applicat…

2024 年中国大学生程序设计竞赛全国邀请赛(郑州)暨第六届CCPC河南省大学生程序设计竞赛 problem K. 树上问题

//先找一个美丽的树&#xff0c;然后遍历树找节点,分析是否符合条件。 //画几个图&#xff0c;思考下。 #include<bits/stdc.h> using namespace std; #define int long long const int n1e611; int a,b,c[n],d,l,r,k,w,an; vector<int>t[n]; void dfs(int x,int…

什么是页分裂、页合并?

数据组织方式 在InnoDB存储引擎中&#xff0c;表数据都是根据主键顺序组织存放的&#xff0c;这种存储方式的表称为索引组织表(index organized table IOT)。 行数据&#xff0c;都是存储在聚集索引的叶子节点上的。而我们之前也讲解过InnoDB的逻辑结构图&#xff1a; 在I…

61、内蒙古工业大学、内蒙科学技术研究院:CBAM-CNN用于SSVEP - BCI的分类方法[脑机二区还是好发的]

前言&#xff1a; 之前写过一篇对CBAM模型改进的博客&#xff0c;在CBAM中引入了ECANet结构&#xff0c;对CBAM中的CAM、SAM模块逐一改进&#xff0c;并提出ECA-CBAM单链双链结构&#xff0c;我的这个小的想法已经被一些同学实现了&#xff0c;并进行了有效的验证&#xff0c;…

快速对比 找出2个名单不同之处

import pandas as pd# 读取两个Excel文件 df1 pd.read_excel(1.xlsx) df2 pd.read_excel(2.xlsx)# 检查两个DataFrame的列是否相同 if list(df1.columns) ! list(df2.columns):print("两个Excel文件的列不一致。")print("文件1的列&#xff1a;", df1.co…

AI智能体|手把手教你申请一个Kimi(Moonshot)的API KEY

大家好&#xff0c;我是无界生长。 今天分享一下如何申请一个Kimi(Moonshot)的API KEY&#xff0c;为后面Kimi(Moonshot)接入微信机器人做铺垫。学会了的话&#xff0c;欢迎分享转发&#xff01; 前提 拥有一个Kimi(Moonshot)账号 使用手机号注册即可&#xff0c;新用户可免费…

【线程创建】——三种方式➕多线程案例练习

02 线程创建 Thread , Runnable , Callable 三种创建方式 Thread class - 继承Thread类 (重点) Runnable接口 - 实现Runnable接口 (重点) Callable接口 - 实现Callable接口 (了解) Thread 类实现 它继承了老祖宗 Object java.lang.Object java.lang.Thread 它实现了 Runnab…

文本到语音的学习笔记:从Docker开始

1.docker 是什么意思&#xff1f; Docker 是一种开源的容器化平台&#xff0c;它允许开发者将应用及其依赖打包到一个轻量级、可移植的容器中&#xff0c;然后可以在任何支持Docker的系统上运行这个应用&#xff0c;而不必担心环境差异导致的问题。 以下是Docker的一些关键特…

【go项目01_学习记录11】

操作数据库 1 文章列表2 删除文章 1 文章列表 &#xff08;1&#xff09;先保证文章已经有多篇&#xff0c;可以直接在数据库中添加&#xff0c;或者访问链接: localhost:3000/articles/create&#xff0c;增加几篇文章。 &#xff08;2&#xff09;之前设置好了articles.ind…

栈队列经典OJ题(详细过程)

1. 有效的括号 - 力扣&#xff08;LeetCode&#xff09; 第一题判断有效的括号&#xff0c;这道题我们会用到栈的知识&#xff0c;栈是后进先出的&#xff0c;可以根据这个来解这道题&#xff0c;先看一下题目和示例。 1.1整体思路 我们通过示例可以看出括号匹配就返回true&am…

【JAVA】BOSS系统发版艺术:构建高效、优雅的微服务部署策略

在现代软件开发领域&#xff0c;微服务架构与容器化部署已迅速成为行业新趋势。微服务架构通过将应用拆分成多个小型、自治的服务单元&#xff0c;每个服务承担某项特定的业务功能。而容器化部署则以其轻量级和高度可移植的特性&#xff0c;为这些微服务的有效打包、分发和运行…

科技查新中的工法查新点如何确立与提炼?案例讲解!

按《工程建设工法管理办法》( 建 质&#xff3b;2014&#xff3d;103 号) &#xff0c;工法&#xff0c;是指以工程为对象&#xff0c;以工艺为核心&#xff0c;运用系 统工程原理&#xff0c;把先进技术和科学管理结合起来&#xff0c;经过一定工程实践形成的综合配套的施工方…

探索美国动态IP池:技术赋能下的网络安全新篇章

在数字化飞速发展的今天&#xff0c;网络安全成为了各行各业关注的焦点。特别是在跨国业务中&#xff0c;如何保障数据的安全传输和合规性成为了企业面临的重要挑战。美国动态IP池作为一种新兴的网络技术&#xff0c;正逐渐走进人们的视野&#xff0c;为网络安全提供新的解决方…

黑马甄选离线数仓项目day02(数据采集)

datax介绍 官网&#xff1a; https://github.com/alibaba/DataX/blob/master/introduction.md DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。 DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre…