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

news2024/11/25 22:52:21

🔥博客主页: 【小扳_-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 。交给其他线程来获取这把“锁”。 

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

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

相关文章

Linux虚拟网络设备:底层原理与性能优化深度解析

在深入探讨Linux虚拟网络设备的底层原理之前&#xff0c;重要的是要理解这些设备如何在Linux内核中实现&#xff0c;以及它们如何与操作系统的其他部分交互以提供高效且灵活的网络功能。虚拟网络设备在现代网络架构中发挥着关键作用&#x1f511;&#xff0c;特别是在云计算☁️…

Playwright录制脚本 —— web自动化测试!

简介&#xff1a; 在编写 web 自动化测试用例时&#xff0c;代码编写的速度是否快&#xff0c;会影响框架的使用体验。现在很多的框架都会提供一些辅助功能&#xff0c;帮助我们更快的去编写自动化测试代码&#xff0c;而录制功能是几乎所有的web自动化工具都会带的功能。在实际…

ASUS华硕灵耀Pro14笔记本AMD锐龙版M7400QC,M7600QA原厂Win11系统工厂包下载

恢复华硕灵耀14PRO出厂开箱状态预装OEM系统Windows11工厂模式安装包&#xff0c;带Recovery恢复还原功能 适用型号&#xff1a; M7400QC、M7400QE、M7400QEB M7600QC、M7600QE、M7600QA、M7600QCB 链接&#xff1a;https://pan.baidu.com/s/1dIGRAKJQLQt_JcKbQWFxJg?pwdbn…

VBA_NZ系列工具NZ05:VBA不打开工作簿获取其内容

我的教程一共九套及VBA汉英手册一部&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到数据库&#xff0c;到字典&#xff0c;到高级的网抓及类的应用。大家在学习的过程中可能会存在困惑&#xff0c;这么多知识点该如何组织…

一文读懂Partisia Blockchain,被严重低估的隐私区块链生态

在今年 3 月&#xff0c;隐私公链 Partisia Blockchain 迎来了重要的进展&#xff0c;该生态通证 $MPC 上线了交易所&#xff0c;目前 $MPC 通证可以在 Kucoin、Gate、BitMart、Bitfinex、Bitture 等平台交易&#xff0c;并将在不久后上线 MEXC 平台。 在上个月上线市场至今&am…

【C++ STL序列容器】deque 双端队列

文章目录 【 1. 基本原理 】【 1. deque 的创建 】1.1 创建一个空的 deque1.2 创建一个 n 个默认值的 deque1.3 创建一个 n 个指定值的 deque1.4 通过一个 deque 初始化另一个 deque1.5 通过基础容器来初始化 queue 容器适配器 【 3. deque 支持的成员函数 】 【 1. 基本原理 】…

【刷题】图论——最小生成树:局域网

要想去除边&#xff0c;并且不改变连通性&#xff0c;而且去除的值最大&#xff0c;相当于保留最小生成树。 注意这题连通块有若干个&#xff0c;所以运行Kruskal相当于形成若干个最小生成树。 如果是prim只能事先处理好各个连通块&#xff0c;然后在连通块内部单独用prim 题目…

电大搜题:为您解锁学习的新大门

近年来&#xff0c;随着社会的不断进步和教育的普及化&#xff0c;广大民众对学习的需求也越来越迫切。在这个信息爆炸的时代&#xff0c;大家对于获取准确、可靠学习资料的渴望日益增长。正是基于这样的背景&#xff0c;黑龙江开放大学&#xff08;简称黑开大&#xff09;与广…

使用Docker定时备份数据

文章目录 一、Docker镜像制作二、MySQL数据备份三、Minio数据备份四、数据跨服务器传输五、Nginx日志分割六、Docker启动七、Docker备份日志 一、Docker镜像制作 镜像制作目录 mc下载地址 - rsyncd.conf # https://download.samba.org/pub/rsync/rsyncd.conf.5port 873 uid …

CMD命令窗口提示文字乱码

我下面说的是日文版系统&#xff0c;中文版会有差异。 一般情况下是 Shiftjis 通常我是用sakura editor来写bat&#xff0c;但是运行后会在cmd窗口出现乱码 test.bat set HENSU这是一个变数 echo %HENSU% pause 执行后出现乱码 原因是不做设置时&#xff0c;command prom…

YOLOV8实战教程——最新安装(截至24.4)

前言&#xff1a;YOLOV8更新比较快&#xff0c;最近用的时候发现有些地方已经跟之前不一样&#xff0c;甚至安装都会出现差异&#xff0c;所以做一个最新版的 yolov8 安装教程 一、Github 或者 GitCode 搜索 ultralytics 下载源码包&#xff0c;下载后解压到你需要安装的位置…

​​​​网络编程学习探索系列之——广播原理剖析

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的网络编程系列之广播原理剖析&#xff0c;在这篇文章中&#xff0c; 你将会学习到如何在网络编程中利用广播来与局域网内加入某个特定广播组的主机&#xff01; 希望这篇文章能对你有所帮助&#xff0c;大家要是觉得我写…

魔方网表ERP mailupdate.jsp 任意文件上传漏洞复现

0x01 产品简介 魔方网表ERP是一款高效、灵活的企业资源规划解决方案,旨在帮助企业实现数智化转型,消除信息孤岛,打造全程一体化的管理体系。魔方网表ERP拥有强大的表单功能和模块化的产品特点,使得企业可以根据自身业务需求,通过简单的拖拽和配置,快速搭建符合自身特点的…

1980-2024考研英语二历年真题及解析

├─01、1980-1997年考研英语真题解析 │ 1980—1985年历年考研英语真题答案集合(无解析).doc │ 1986—1997年历年考研英语真题集.doc │ 1986—1997年历年考研英语真题答案解析合集.pdf │ ├─02、1998-2009年考研英语真题解析 │ │ ? 1998-2009考…

拥有一台阿里云服务器可以做什么?

阿里云ECS云服务器可以用来做什么&#xff1f;云服务器可以用来搭建网站、爬虫、邮件服务器、接口服务器、个人博客、企业官网、数据库应用、大数据计算、AI人工智能、论坛、电子商务、AI、LLM大语言模型、测试环境等&#xff0c;云服务器吧yunfuwuqiba.com整理阿里云服务器可以…

【架构师】应用架构图

技术架构是应接应用架构的技术需求&#xff0c;并根据识别的技术需求&#xff0c;进行技术选项&#xff0c;把各个关键技术和技术之间的关系描述清楚。 基础结构解决的主要问题包括&#xff1a;如何进行技术层面的分层、开发框架的选择、开发语言的选择、涉及非功能性需求的技…

ObjectiveC-第一部分-基础入门-学习导航

专题地址:MacOS一站式程序开发系列专题 第一部分:基础入门学习导航 OSX-01-Mac OS应用开发概述:简单介绍下MacOS生态、Xcode使用以及使用Xcode创建app的方法OSX-02-Mac OS应用开发系列课程大纲和章节内容设计:介绍下此系列专题的文章内容组织形式以及此系列专题的覆盖内容…

极海APM32电机驱动板记录(二)

文章目录 1、解除写保护2、极海驱动板资源概述3、新建工程4、点灯5、嘀嗒定时器6、中断7、串口打印8、adc读取9、i2c尝试10、定时器测试11、电机驱动pwm测试 上一篇文章算是简单了解了一下极海的板子开发环境吧&#xff0c;结果前几天板子来了&#xff0c;然后发现一个大bug&am…

【Linux】Socket编程接口 | 实现简单的UDP网络程序

文章目录 一、预备知识理解源IP地址和目的IP地址理解源mac地址和目的mac地址认识端口号理解源端口号和目的端口号理解“端口号&#xff08;PORT&#xff09;”和“进程ID&#xff08;PID&#xff09;” 认识TCP和UDP协议TCP协议UDP协议 网络字节序为什么网络字节序采用的是大端…

使用 npm 工具高效更新项目依赖包

团队内部会用工具定时检查包的最新版本并通知&#xff0c;以便我们及时跟进社区进展&#xff0c;避免和技术栈出现版本脱节导致无法使用最新特性和优化内容 这里只说明手动查看和更新包的主要几个命令。 npm outdated&#xff1a;检查项目中过时的依赖包及其最新版本。 npm i…