JavaEE 初阶篇-深入了解 CAS 机制与12种锁的特征(如乐观锁和悲观锁、轻量级锁与重量级锁、自旋锁与挂起等待锁、可重入锁与不可重入锁等等)

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 乐观锁与悲观锁概述

        1.1 悲观锁(Pessimistic Locking)

        1.2 乐观锁(Optimistic Locking)

        1.3 区别与适用场景

        2.0 轻量级锁与重量级锁概述

        2.1 真正加锁的底层逻辑顺序

        2.2 轻量级锁

        2.3 重量级锁

        2.4 区别于适用场景

        2.5 轻量级锁与乐观锁的区别、重量级锁与悲观锁的区别

        3.0 自旋锁与挂起等待锁概述

        3.1 自旋锁(Spin Lock)

        3.2 挂起等待锁(Suspend-Resume Lock)

        3.3 自旋锁是一种典型的轻量级锁的实现方式

        4.0 公平锁与非公平锁概述

        4.1 公平锁(Fair Lock)

        4.2 非公平锁(Unfair Lock)

        5.0 可重入锁与不可重入锁概述

        6.0 读写锁与互斥锁概述

        6.1 读写锁(Read-Write Lock)

        6.2 互斥锁(Mutex Lock)

        7.0 CAS 概述

        7.1 CAS 的实际应用

        7.1.1 CAS 的实际应用 - 实现原子类

        7.1.2 CAS 的实际应用 - 自旋锁


        1.0 乐观锁与悲观锁概述

        乐观锁和悲观锁是两种并发控制的策略,用于处理多线程环境下的数据访问和更新。它们的主要区别在于对并发情况的预期和处理方式。

        synchronized 是乐观锁也悲观锁。

        1.1 悲观锁(Pessimistic Locking)

        悲观锁的基本思想是在操作数据之前先获取锁,假定会并发访问,因此在整个操作过程中都持有锁,以防其他线程对数据进行修改。悲观锁通常会导致其他线程在访问数据时被阻塞,以确保数据的一致性。

        简单来说,在多线程中对共享变量进行操作时,会认为其他线程都会对这个共享变量进行操作,因此,从悲观锁的角度来说,先加锁成功后,才能对共享变量进行操作;否则,只能阻塞等待锁释放。从而可以确保线程安全。

        常见的悲观锁:synchronized 关键字、ReentrantLock 等锁机制。

        1.2 乐观锁(Optimistic Locking)

        乐观锁的基本思想是假设在数据操作过程中不会发生并发冲突,因此不会立即加锁,而是在更新数据时检查是否有其他线程已经对数据进行修改了。如果没有发现数据被修改,那么继续操作;如果发现数据已经被修改了,会进行回滚或者重试。

        简单来说,在多线程中对共享变量进行操作时,一开始不会认为有其他线程会对该共享变量进行操作,认为当前线程可以安心的对该变量进行操作。执行到后面,如果发现已经有其他线程对该共享变量进行了操作了,那么当前的线程执行回滚或者重试。

        1.3 区别与适用场景

        synchronized 一开始是使用乐观锁策略,当发现锁竞争比较频繁的时候,就会自动切换悲观锁策略。

        悲观锁:真正加上了锁处理,适用于并发写入较多、冲突概率较高的场景,适合长事务处理。

        乐观锁:没有真正的加锁处理,适用于并发读取较多,冲突概率较低的场景,适合短事务处理。

        2.0 轻量级锁与重量级锁概述

        轻量级锁和重量级锁是 Java 中用于实现同步的两种锁机制,用于保护共享资源在多线程环境下的访问。它们的设计目的是为了在不同情况下提供更高效的并发控制。

        synchronized 是轻量级锁也是重量级锁。

        2.1 真正加锁的底层逻辑顺序

        CPU 提供了原子操作指令给操作系统,操作系统提供了 mutex 互斥锁给 JVM ,接着 JVM 将锁封装成了 synchronized 中的悲观锁、ReenTrantLock 等。

        2.2 轻量级锁

        加锁机制尽可能不适用 mutex ,而是尽量在用户态代码中完成,实在锁竞争太大、锁冲突太大了,再转换为重量级,即使用 mutex 。

        轻量级锁是一种乐观锁机制,用于优先低竞争情况下的同步操作。当一个线程尝试获取锁是,如果锁没有被其他线程占用,会将对象的 Mark Word 指向当前线程,将对象状态标记为“偏向锁”。

        2.3 重量级锁

        加锁机制重度依赖了 OS 提供了 mutex ,大量的内核态用户态切换,很容易引发线程的调度。两个操作成本比较高,一旦涉及到用户态和内核态的切换,就意味着“沧海桑田”。

        重量级锁是一种悲观锁机制,用于处理高竞争情况下的同步操作。当多个线程竞争同一把锁时,会将锁升级为重量级锁,线程会被阻塞,进入阻塞状态。

        2.4 区别于适用场景

        synchronized 开始是一个轻量级锁。如果锁冲突比较严重,就会变成重量级锁。

        轻量级锁:适用于低竞争情况下的同步操作,提高了并发性能,但在高竞争情况下会升级为重量级锁。

        重量级锁:适用于高竞争情况下的同步操作,保证了数据的一致性,但在低竞争情况下会带来额外的开销。

在实际应用中,Java虚拟机会根据当前线程的竞争情况动态地选择轻量级锁或重量级锁来进行同步操作,以提高系统的并发性能和数据一致性。

        2.5 轻量级锁与乐观锁的区别、重量级锁与悲观锁的区别

轻量级锁:

        轻量级锁是一种乐观锁,它尝试使用 CAS(Compare and Swap)等原子操作来尝试获取锁,避免了线程阻塞和内核态操作,因此被称为轻量级。如果 CAS 操作成功,线程就成功获取了锁,如果失败,则会升级为重量级锁或其他适合的锁机制。轻量级锁并不是没有真正的加锁,而是通过乐观的方式尝试获取锁,避免了一些开销较大的操作。

重量级锁:

        重量级锁是一种悲观锁,它通常会涉及到线程的阻塞、唤醒和操作系统的调度等操作,因此被称为重量级。当多个线程竞争锁时,重量级锁会导致线程进入阻塞状态,等待其他线程释放锁后才能继续执行。重量级锁会涉及到真正的加锁操作,包括线程的阻塞和唤醒等。

        3.0 自旋锁与挂起等待锁概述

        自旋锁和挂起等待锁是两种不同的锁机制,它们在处理线程同步和互斥时有不同的实现方式和特点。

        synchronized 是自旋锁也是挂起锁。

        3.1 自旋锁(Spin Lock)

        自旋锁是一种基于忙等待的锁机制,当一个线程尝试获取锁时,如果发现锁已经被其他线程占用,它会一直循环检查锁的状态(自旋)直到锁可用。自旋锁适用于锁被占用时间较短的情况,因为它可用减少线程切换的开销。但是如果锁被长时间占用,自旋锁会导致线程长时间占用 CPU 资源而无法进展,造成性能问题。

        简单来说,自旋锁一直会占用 CPU 资源,所谓的“空转”、“忙等待”,只要锁被释放了,那么自旋锁就会立马获取锁,效率高。

        按照之前的方式,线程再抢锁失败后,进入阻塞状态,放弃 CPU ,需要过很久再次被调度,但实际上,大部分情况下,虽然当前线程抢锁失败,但过不了多久,锁就会被释放。这样就没有必要放弃 CPU 资源。这时候就可以适用自旋锁来处理这样的问题。

        3.2 挂起等待锁(Suspend-Resume Lock)

        挂起等待锁是一种基于线程阻塞和唤醒的锁机制,当一个线程尝试获取锁时,如果发现锁已经被占用,它会被挂起阻塞等待其他线程释放锁。当锁可用时,其他线程会唤醒被挂起的线程继续执行。

        挂起等待锁适用于锁被占用时间长的情况,因为它可以避免线程忙等待占用 CPU 资源,但是会引入线程切换的开销。

        3.3 自旋锁是一种典型的轻量级锁的实现方式

        synchronized 中的轻量级锁策略大概率就是通过自旋锁的方式实现的。

优点:没有放弃 CPU ,不涉及线程阻塞和调度,一旦锁被释放,就能第一时间获取到锁。

缺点:如果锁被其他线程持有的时间比较长,那么就会持续的消耗 CPU 资源。(而挂起等待的时候是不会消耗 CPU 资源的)。

        相对应的,挂起等待锁是一种典型的重量级锁的实现方式。

        4.0 公平锁与非公平锁概述

        公平锁和非公平锁是两种不同的锁策略,它们主要影响了锁的获取顺序和公平性。

        synchronized 是非公平锁。ReenTrantLock 默认是非公平锁,可以转换成公平锁。

ReentrantLock fairLock = new ReentrantLock(true); // 创建公平锁

        通过将参数设置为 true,可以创建一个公平锁;而默认情况下参数为 false,创建的是非公平锁。

        4.1 公平锁(Fair Lock)

        公平锁是一种保证锁的获取按照请求的顺序进行的锁。当一个线程请求一个公平锁时,如果锁当前被其他线程占用,该线程会进入等待队列,按照先来先服务的原则等待获取锁。当锁释放时,等待时间最长的线程会被唤醒并获取锁。公平锁能够保证线程按照请求的顺序获取锁,避免了线程饥饿的问题。

        4.2 非公平锁(Unfair Lock)

        非公平锁是一种允许锁获取竞争策略,它允许新请求的线程直接尝试获取锁,而不考虑等待队列中的线程顺序。如果锁当前被其他线程占用,新请求的线程会直接尝试获取锁,而不会进入等待队列。这种策略可能会导致某些线程长时间无法获取锁,造成线程饥饿的问题。

        5.0 可重入锁与不可重入锁概述

        可重入锁的字面意思是“可以重新进入的锁”,即允许同一个线程多次获取同一把锁。比如一个递归函数里面加锁操作,递归过程中这个锁会阻塞自己吗?如果不会,那么这个锁就是可重入锁(因为这个原因可重入锁也叫做递归锁)。

        Java 里只要以 ReenTrant 开头命名的锁都是可重入锁,而且 JDK 提供的所有现成的 Lock 实现类,包括 synchronized 关键字锁都是可重入锁。

        而 Linux 系统提供的 mutex 是不可重入锁。

        6.0 读写锁与互斥锁概述

        读写锁简单来说,读操作与读操作并发执行中不会加锁,读操作与写操作并发操作中会加锁,写操作与写操作并发操作中会加锁。

        互斥锁简单来说,无论进行哪一种操作,并发执行的操作都需要加上锁。

        synchronized 不是读写锁,是互斥锁。

        ReenTrantLock 不是读写锁,是互斥锁。

ReenTrantLock 使用代码演示:

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();
    private int sharedData = 0;

    public void incrementData() {
        lock.lock();
        try {
            sharedData++;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ReentrantLockExample example = new ReentrantLockExample();

        // 创建多个线程并发执行 incrementData 方法
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.incrementData();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.incrementData();
            }
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final shared data value: " + example.sharedData);
    }
}

        6.1 读写锁(Read-Write Lock)

        读写锁允许多个线程同时读取共享资源,但在有写操作时需要互斥访问。

读操作:

        多个线程可以同时获取读锁,并发执行读操作,不会互斥。

写操作:

        写锁是互斥的,即写操作与任何其他操作或写操作都是互斥的。当有线程持有写锁时,其他线程无法获取读锁或者写锁,直到写操作释放写锁。

        ReentrantReadWriteLock.ReadLock 类表示一个读锁,这个对象提供了 lock/unlock 方法进行加锁解锁。

        ReentrantReadWriteLock.WriteLock 类表示一个写锁,这个对象提供了 lock/unlock 方法进行加锁解锁。

代码如下:

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class MyReentrantLock {

    private int data = 100;
    private final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
    public int readData(){
        readLock.lock();
        try {
            return data;
        }finally {
            readLock.unlock();
        }
    }

    public void writeData(int data){
        writeLock.lock();
        try {
            this.data = data;
        }finally {
            writeLock.unlock();
        }
    }
}
public class demo1 {
    public static void main(String[] args) {
        MyReentrantLock myReentrantLock = new MyReentrantLock();
        //读取数据
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "-->读取数据为: " + myReentrantLock.readData());
            }).start();

        }

        //写数据
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
               myReentrantLock.writeData(1);
            }).start();
        }

        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "-->读取数据为: " + myReentrantLock.readData());
            }).start();

        }
    }
}

        6.2 互斥锁(Mutex Lock)

        互斥锁是一种常见的锁机制,用于保护共享资源的互斥访问。无论是读操作还是写操作,都要获取互斥锁才能访问共享资源。

        7.0 CAS 概述

        CAS(Compare and Swap)是一种并发控制机制,通常用于实现无锁算法。它主要用于解决多线程并发访问共享数据时的原子性操作问题。

        CAS 的基本原理是利用 CPU 提供的原子性指令来实现无锁的原子操作。当多个线程同时尝试执行 CAS 操作时,只有一个线程会成功,其他线程会失败并重试。

CAS 操作包括三个步骤:

        1)比较(Compare):首先, CAS 会比较当前内存中的值和预期值是否相等。

        2)交换(Swap):如果相等,CAS 就会将新值写入到主内存中;否则,不做任何操作。

        3)返回(Return):CAS 操作会返回操作是否成功的结果,通常时一个布尔值。

        这三个操作都是原子性的,因此不存在线程安全问题。

图解:

        7.1 CAS 的实际应用

        原子操作、非阻塞算法、自旋锁、ABA 问题的解决、乐观锁的实现等。

        7.1.1 CAS 的实际应用 - 实现原子类

        CAS 可以用于实现原子操作,比如 AtomicInteger、AtomicLong 等原子类都是基于 CAS 实现的。在多线程环境下,通过 CAS 可以确保对共享变量的操作是原子的,避免了使用锁带来的性能开销。

        比如说 AtomicInteger 类,是基于 CAS 的思想实现的,假如有一个 AtomicInteger 的实例对象,在多线程中,对该实例对象进行 +1 操作,一般来说,如果不加锁的话,会出现线程安全问题,但是对于当前对象来说,即使不用加锁,也不用出现线程安全问题。

代码如下:

import java.util.concurrent.atomic.AtomicInteger;

public class MyAtomicInteger {
    private AtomicInteger count = new AtomicInteger(0);

    public void add(){
        System.out.println(count.incrementAndGet());
    }
}
public class Text {
    public static void main(String[] args) {
        MyAtomicInteger myAtomicInteger = new MyAtomicInteger();
        for (int i = 0; i < 1000; i++) {
            new Thread(()->{
                for (int j = 0; j < 5000; j++) {
                    myAtomicInteger.add();
                }
            }).start();
        }
    }
}

运行结果如下:

        运行结果是正确的,没有出现线程安全问题。

        这是为什么即使没有加上锁也不会出现线程安全问题呢?

        答案就在用了 AtomicInteger 修饰的变量,且 +1 操作用到了 count.incrementAndGet() 实例方法。

详细对 count.incrementAndGet() 方法进行分析:

        该方法中还包含了 getAndAddInt() 方法,第一个参数是代表着当前对象,第二个参数可以认为是存放值的地址,第三个参数默认为 1 。

进入 getAndAddInt() 方法进行分析:

         参数 o 代表着当前对象,参数 offset 代表着值的地址,参数 delta 为 1 。该方法中内部定义了一个变量 v ,通过 getIntVolatile() 这个方法,用当前的对象还有值的地址获取到最新的数据赋值给 v 。再接着通过 weakCompareAndSetInt() 方法,来比较当前的 v 跟之前获取的 v 的值是否相同,如果相同,代表着没有线程访问这个数据,只有当前线程正在访问,那么就可以对这个数据进行修改,再返回到主内存中;如果不相同,代表有其他线程访问这个数据,此时不能直接将当前线程更新的值放到主内存中,会出现线程安全问题,因此重复循环,再来新一轮,先获取主内存中最新的数据,在来比较当前数据与之前获取到的 v 是否相同...一直循环往复。直到当前数据与之前的获取到的 v 相同,那么就可以将值放入到内存中。

进入 weakCompareAndSetInt() 方法进行分析:

        如果成功就返回 true,否则返回 false 。 

        最后,可以清楚的了解到以上这个思想跟 CAS 的机制是一致的。

        7.1.2 CAS 的实际应用 - 自旋锁

        自旋锁是基于 CAS 机制实现更灵活的锁,获取到更多的控制权。

伪代码:

public class MySpinLock {
    private  Thread ower = null;

    public void lock(){
        //通过 CAS 看当前锁是否被某个线程持有
        //如果这个锁已经被别的线程持有,那么就自旋等待。
        //如果这个锁没有被别的线程持有,那么就把 ower 设为当前尝试加锁的线程。
        while (!CAS(this.ower,null,Thread.currentThread())){

        }
    }

    public void unlock(){
        this.ower = null;
    }
}

        结合自旋锁的特点和 CAS 机制来分析,线程只要没有获取的锁,就会一直占用 CPU 资源等待,直到锁释放为止,如何来判断锁是否被占用呢?

        就可以通过 CAS 机制来判断,大概流程是:判断当前的线程 ower 是否为 null ,如果是,则将 ower 修改为当前线程所持有,这样来看,其他线程也会通过 CAS 机制来判断当前的 ower 是否否为 null ,返回结果为 false ,则只能空转了,等待当前线程释放锁,此时释放锁会把 ower 赋值为 null 。交给其他线程来获取这把“锁”。 

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

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

相关文章

我企业的业务需要制作企业网站吗?11个支持的理由以及5个反对的理由!

如果你的企业经营得还不错&#xff0c;你可能会找出很多理由&#xff0c;说明为什么一个高效的网站对你来说并不那么重要。确实&#xff0c;你明白企业需要在互联网上有一定的存在感&#xff0c;但你可能并不认为一个高效的网站会对你的特定业务产生太大的影响——尤其是当你已…

实战纪实 | 编辑器漏洞之Ueditor-任意文件上传漏洞 (老洞新谈)

UEditor 任意文件上传漏洞 前言 前段时间在做某政府单位的项目的时候发现存在该漏洞&#xff0c;虽然是一个老洞&#xff0c;但这也是容易被忽视&#xff0c;且能快速拿到shell的漏洞&#xff0c;在利用方式上有一些不一样的心得&#xff0c;希望能帮助到一些还不太了解的小伙…

PCIe总线-存储器域和PCIe总线域访问流程(二)

1.概述 PCIe总线的最大特点是像CPU访问DDR一样&#xff0c;可以直接使用地址访问PCIe设备&#xff08;桥&#xff09;&#xff0c;但不同的是DDR和CPU同属于存储器域&#xff0c;而CPU和PCIe设备属于两个不同的域&#xff0c;PCIe设备&#xff08;桥&#xff09;的地址空间属于…

[RK3399 Linux] 使用busybox 1.36.1制作rootfs

一、 编译、安装、配置 busybox 1.1 下载源码 根文件系统是根据busybox来制作的。 下载地址:https://busybox.net/downloads/。 这里就以1.36.1版本为例进行编译安装介绍: 注意:编译linux内核与文件系统中的所有程序要使用相同的交叉编译器。 下载完成后解压: mkdir …

03 SQL基础 -- 查询与运算符

一、SELECT 语句基础 1.1 从表中选取数据 SELECT 语句 从表中选取数据时需要使用SELECT语句,也就是只从表中选出(SELECT)必要数据的意思。通过SELECT语句查询并选取出必要数据的过程称为匹配查询或查询(query) 基本SELECT语句包含了SELECT和FROM两个子句(clause)。示…

NAT实验

要求&#xff1a; 1、AR2为ISP路由器&#xff0c;其上只能配置IP地址&#xff0c;不得再进行其他的任何配置 2、PC1-PC2可以ping通客户平板和DNS服务器&#xff1b; 3、客户端可以通过域名访问http1&#xff0c;通过地址访问http2 4、R1为边界路由器&#xff0c;其上只有一…

计算机视觉工程师

为进一步贯彻落实中共中央印发《关于深化人才发展体制机制改革的意见》和国务院印发《关于“十四五”数字经济发展规划》等有关工作的部署要求&#xff0c;深入实施人才强国战略和创新驱动发展战略&#xff0c;加强全国数字化人才队伍建设&#xff0c;持续推进人工智能从业人员…

【深度学习】深度学习md笔记总结第4篇:TensorFlow介绍,学习目标【附代码文档】

深度学习笔记完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;深度学习课程&#xff0c;深度学习介绍要求,目标,学习目标,1.1.1 区别,学习目标,学习目标。TensorFlow介绍&#xff0c;2.4 张量学习目标,2.4.1 张量(Tensor),2.4.2 创建张量的指令,2.4.3 张量…

C语言世界上最详细自定义类型:联合和枚举

前言&#xff1a; hello! 大家好&#xff0c;我是小陈&#xff0c;今天给大家带来一篇联合和枚举的博客&#xff01;&#xff01;&#xff01; 1.联合体类型的声明 像结构体⼀样&#xff0c;联合体也是由⼀个或者多个成员构成&#xff0c;这些成员可以不同的类型。 但是编译…

安装达梦(DM8)数据库(图形化安装)

一、配置DM8数据库系统环境 在CentOS7系统环境安装DM8&#xff08;达梦&#xff09;数据库前的准备。&#xff08;注意&#xff1a;安装前必须创建 dmdba 用户&#xff0c;禁止使用 root 用户安装数据库。&#xff09; 1、新建用户 运行SecureCRT工具&#xff0c;root登录16…

域控软件安全隔离关键技术剖析:MCU域 VS SOC域

安全隔离的需求 功能安全开发中&#xff0c;软件阶段由软件V模型左边的软件安全需求SSR开始。SSR是从技术安全需求TSR中提取出软件的功能安全需求&#xff0c;大多数情况下具有不同的ASIL等级。 图1 功能安全软件开发V模型 随后&#xff0c;软件安全需求会被分配到软件架构中的…

利用SARscape对日本填海造陆和天然气开采进行地表形变监测

日本千叶市&#xff0c;是日本南部重要的工业港市。位于西部的浦安市是一个典型的"填海造田"城市&#xff0c;东南部的东金区有一片天然气开采区域&#xff0c;本文利用SARscape&#xff0c;用干涉叠加的方法&#xff0c;即PS和SBAS&#xff0c;对这两个区域进行地表…

36-代码测试(上):如何编写Go语言单元测试和性能测试用例?

每种语言通常都有自己的测试包/模块&#xff0c;Go语言也不例外。在Go中&#xff0c;我们可以通过testing包对代码进行单元测试和性能测试。 如何测试 Go 代码&#xff1f; Go语言有自带的测试框架testing&#xff0c;可以用来实现单元测试&#xff08;T类型&#xff09;和性…

Point-Nerf复现及解析

Point-Nerf复现及解析 鸣谢&#xff1a;同组的李xx师兄博士(交流思路)、辰昶仪器的狗哥等人&#xff08;帮忙down资源&#xff09; 0.0我自己的复现工程0.1相关库介绍0.1.1 pytorch0.1.2 h5py0.1.3 Scikit-Image0.1.4 imageio0.1.5 scipy0.1.6 Matplotlib0.1.7 fonttools 0.2…

JAVA的学习日记DAY6

文章目录 数组例子数组的使用数组的注意事项和细节练习数组赋值机制数组拷贝数组反转数组添加 排序冒泡排序 查找多维数组 - 二维数组二维数组的使用二维数组的遍历杨辉三角二维数组的使用细节和注意事项练习 开始每日一更&#xff01;得加快速度了&#xff01; 数组 数组可以…

16. 网络编程(1)

Hi,大家好!从本节开始我们学习网络编程相关的知识。基于TCP服务器和客户端实现流程框架。 本节目录: 网络编程在软件开发中具有相当重要的作用,涉及到各方各面: 网络通信: Linux系统作为一个多用户、多任务的操作系统,网络通信是其重要的功能之一。通过网络编程,可以实现…

稀碎从零算法笔记Day46-LeetCode:互质树

这几天有点懈怠了 题型&#xff1a;树、DFS、BSF、数学 链接&#xff1a;1766. 互质树 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给你一个 n 个节点的树&#xff08;也就是一个无环连通无向图&#xff09;&#xff0c;节点编号从 0 到 …

5款好用又免费的UI设计软件

之前我们分享了五款好用的制作原型的工具&#xff0c;制作完了原型&#xff0c;就要对界面进行优化&#xff0c;这个时候就是 UI 设计师的任务了&#xff0c;UI 设计软件对于设计师们来说是很重要的&#xff0c;UI 设计工具是否好用直接影响到最后结果的好坏&#xff0c;那么就…

[lesson20]初始化列表的使用

初始化列表的使用 类成员的初始化 C中提供了初始化列表对成员变量进行初始化 语法规则 注意事项 成员的初始化顺序与成员的声明顺序相同成员的初始化顺序与初始化列表中的位置无关初始化列表先于构造函数的函数体执行 类中的const成员 类中的const成员会被分配空间的类中…

Baichuan-7B-chat WebDemo 部署调用

Baichuan-7B-chat WebDemo 部署调用 Baichuan2 介绍 Baichuan 2 是百川智能推出的新一代开源大语言模型&#xff0c;采用 2.6 万亿 Tokens 的高质量语料训练。在多个权威的中文、英文和多语言的通用、领域 benchmark 上取得同尺寸最佳的效果。 环境准备 在autodl平台中租一…