【Java多线程】3——Lock API控制多线程

3 Lock API控制多线程

⭐⭐⭐⭐⭐⭐
Github主页👉https://github.com/A-BigTree
笔记仓库👉https://github.com/A-BigTree/tree-learning-notes
个人主页👉https://www.abigtree.top
⭐⭐⭐⭐⭐⭐


如果可以,麻烦各位看官顺手点个star~😊

如果文章对你有所帮助,可以点赞👍收藏⭐支持一下博主~😆


文章目录

  • 3 Lock API控制多线程
    • 3.1 HelloWorld
      • 3.1.1 买票
      • 3.1.2 需要注意的点
        • 确保锁被释放
        • 加锁和解锁操作对称执行
        • 避免锁对象的线程私有化
    • 3.2 Lock接口
    • 3.3 可重入锁
      • 3.3.1 基本用法
      • 3.3.2 验证可重入性
      • 3.3.3 `tryLock()`
      • 3.3.4 `tryLock(time,timeUnit)`
      • 3.3.5 公平锁
        • 概念
        • 代码
        • 使用建议
      • 3.3.6 `lockInterruptibly()`
        • 相应中断
        • synchronized方式下的阻塞状态无法打断
        • `lockInterruptibly()`
    • 3.4 读写锁
      • 3.4.1 读写锁介绍
        • 概念
        • 进入条件
        • 重要特征
      • 3.4.2 `ReadWriteLock`接口
      • 3.4.3 `ReentrantReadWriteLock`类结构
        • 总体结构图
      • 3.4.4 典型案例
    • 3.5 线程间通信
      • 3.5.1 核心语法
      • 3.5.2 案例演示
        • 代码实现
      • 3.5.3 定制化通信
        • 语法基础
        • 案例
    • 3.6 Lock与synchronized对比
      • 3.6.1 相同点
      • 3.6.2 不同点
      • 3.6.3 使用建议
        • 从功能效果的角度
        • 从开发便捷性的角度
        • 从性能角度
        • 使用建议

3.1 HelloWorld

3.1.1 买票

public class Demo01HelloWorld {// 声明成员变量维护票库存private int stock = 100;// 创建锁对象// 变量类型:java.util.concurrent.locks.Lock 接口// 对象类型:Lock 接口的最常用的实现类 ReentrantLockprivate Lock lock = new ReentrantLock();// 声明卖票的方法public void saleTicket() {try {// 加锁lock.lock(); // synchronized (this) {if (stock > 0) {// 卖票的核心操作System.out.println(Thread.currentThread().getName() + " 卖了一张,还剩 " + --stock + " 张票。");} else {System.out.println(Thread.currentThread().getName() + " 卖完了。");}} catch (Exception e) {e.printStackTrace();} finally {// 解锁lock.unlock(); // }}}public static void main(String[] args) {// 1、创建当前类对象Demo01HelloWorld demo = new Demo01HelloWorld();// 2、开启三个线程调用卖票方法new Thread(()->{for (int i = 0; i < 40; i++) {demo.saleTicket();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {}}}, "thread-01").start();new Thread(()->{for (int i = 0; i < 40; i++) {demo.saleTicket();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {}}}, "thread-02").start();new Thread(()->{for (int i = 0; i < 40; i++) {demo.saleTicket();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {}}}, "thread-03").start();}}

3.1.2 需要注意的点

确保锁被释放

使用 Lock API 实现同步操作,是一种面向对象的编码风格。这种风格有很大的灵活性,同时可以在常规操作的基础上附加更强大的功能。但是也要求编写代码更加谨慎:如果忘记调用 lock.unlock() 方法则锁不会被释放,从而造成程序运行出错。

加锁和解锁操作对称执行

不管同步操作是一层还是多层,有多少个加锁操作,就应该相应的有多少个解锁操作。

避免锁对象的线程私有化

锁对象如果是线程内部自己创建的,是自己独占的,其它线程访问不到这个对象,那么这个锁将无法实现**『排他』**效果,说白了就是:锁不住。

3.2 Lock接口

全类名:java.util.concurrent.locks.Lock

方法功能说明:

方法名功能
void lock()加同步锁,如果没有得到锁会一直等
void unlock()解除同步锁
boolean tryLock()尝试获取锁。如果没有获取到则立即返回,不做任何等待
返回 true:表示获取成功
返回 false:表示获取失败
boolean tryLock(long time, TimeUnit unit)尝试获取锁,且等待指定时间
返回 true:表示获取成功
返回 false:表示获取失败
void lockInterruptibly()以『支持响应中断』的模式获取锁
Condition newCondition()获取用于线程间通信的 Condition 对象

3.3 可重入锁

全类名:java.util.concurrent.locks.ReentrantLock

这是 Lock 接口最典型、最常用的一个实现类。

3.3.1 基本用法

基本要求1:将解锁操作放在 finally 块中,确保解锁操作能够被执行到。

基本要求2:加锁和解锁操作要对称。

try {// 加锁lock.lock();// 同步代码部分
} catch(Exception e) {// ...
} finally {// 解锁lock.unlock();
}

3.3.2 验证可重入性

// 测试目标:验证可重入性
// 测试方式:在同一个线程内,嵌套使用 try ... catch ... finally 结构
// 由于可重入性的大前提就是已经加了一个锁,然后再加一个锁,所以不可能有多个线程,就在 main 线程里测试即可
// 测试标准:线程不会被自己锁住,不会陷入死锁就证明当前使用的 API 支持可重入
// 创建锁对象
Lock lock = new ReentrantLock();try {// 外层加锁操作lock.lock();System.out.println(Thread.currentThread().getName() + " 外层加锁成功。");try {// 内层加锁操作lock.lock();System.out.println(Thread.currentThread().getName() + " 内层加锁成功。");} finally {// 内层解锁操作lock.unlock();System.out.println(Thread.currentThread().getName() + " 内层解锁成功。");}} finally {// 外层解锁操作lock.unlock();System.out.println(Thread.currentThread().getName() + " 外层解锁成功。");
}

3.3.3 tryLock()

public class Demo03TryLock {private Lock lock = new ReentrantLock();public void showMessage() {boolean lockResult = false;try {// 尝试获取锁// 返回true:获取成功// 返回false:获取失败lockResult = lock.tryLock();if (lockResult) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {}System.out.println(Thread.currentThread().getName() + " 得到了锁,正在工作");} else {System.out.println(Thread.currentThread().getName() + " 没有得到锁");}}catch (Exception e){e.printStackTrace();}finally {// 如果曾经得到了锁,那么就解锁if (lockResult) {lock.unlock();}}}public static void main(String[] args) {// 1、创建多个线程共同操作的对象Demo03TryLock demo = new Demo03TryLock();// 2、创建三个线程new Thread(()->{for(int i = 0; i < 20; i++) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {}demo.showMessage();}}, "thread-01").start();new Thread(()->{for(int i = 0; i < 20; i++) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {}demo.showMessage();}}, "thread-02").start();new Thread(()->{for(int i = 0; i < 20; i++) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {}demo.showMessage();}}, "thread-03").start();}}

3.3.4 tryLock(time,timeUnit)

public class Demo04TryLockWithTime {private Lock lock = new ReentrantLock();// 得到锁之后占用 5 秒public void useLock() {try {lock.lock();System.out.println(Thread.currentThread().getName() + " 开始工作");try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {}System.out.println(Thread.currentThread().getName() + " 结束工作");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}// 在尝试获取锁的过程中,可以等待一定时间public void waitLock() {boolean lockResult = false;try {// 尝试获取锁,并指定了等待时间lockResult = lock.tryLock(3, TimeUnit.SECONDS);if (lockResult) {System.out.println(Thread.currentThread().getName() + " 得到了锁,开始工作");} else {System.out.println(Thread.currentThread().getName() + " 没有得到锁");}}catch (Exception e){e.printStackTrace();}finally {if (lockResult) {lock.unlock();}}}public static void main(String[] args) {// 1、创建当前类对象Demo04TryLockWithTime demo = new Demo04TryLockWithTime();// 2、创建 A 线程占用锁new Thread(()->{demo.useLock();}, "thread-a").start();// 3、创建 B 线程尝试获取锁new Thread(()->{demo.waitLock();}, "thread-b").start();}}

3.3.5 公平锁

概念

ReentrantLock 构造器中传入 boolean 类型的参数:

  • true:创建公平锁(在锁上等待最长时间的线程有最高优先级);
  • false:创建非公平锁;
代码
public class Demo05FairLock {private Lock lock = new ReentrantLock(true);public void printMessage() {try {lock.lock();System.out.println(Thread.currentThread().getName() + " say hello to you");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {}}catch (Exception e) {e.printStackTrace();}finally {lock.unlock();}}public static void main(String[] args) {// 1、创建当前类的对象Demo05FairLock demo = new Demo05FairLock();// 2、创建三个线程,每个线程内调用 printMessage() 方法十次new Thread(()->{for (int i = 0; i < 10; i++) {demo.printMessage();}}, "thread-a").start();new Thread(()->{for (int i = 0; i < 10; i++) {demo.printMessage();}}, "thread-b").start();new Thread(()->{for (int i = 0; i < 10; i++) {demo.printMessage();}}, "thread-c").start();}}
使用建议
  • 公平锁对线程操作的吞吐量有限制,效率上不如非公平锁;
  • 如果没有特殊需要还是建议使用默认的非公平锁。

3.3.6 lockInterruptibly()

lock:动词,加锁的动作

Interruptibly:修饰动词的副词,表示可以被打断 组合起来的含义:以可以被打断的方式加锁。

具体来说就是如果线程是被 lockInterruptibly() 加的锁给阻塞的,那么这个阻塞状态可以被打断。

相应中断

在这里插入图片描述

synchronized方式下的阻塞状态无法打断

synchronized 导致的 blocked 状态不支持响应中断。

在这里插入图片描述

lockInterruptibly()

在这里插入图片描述

public class Demo07LockInterruptibly {private Lock lock = new ReentrantLock();// 小强:持续占用锁。public void useLock() {try {lock.lock();while (true) {System.out.println(Thread.currentThread().getName() + " 正在占用锁");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {}}}catch (Exception e) {e.printStackTrace();}finally {lock.unlock();}}// 小明:痴痴地等待小强释放锁public void waitLock() {System.out.println(Thread.currentThread().getName() + " 线程启动了");try {// 通过 lockInterruptibly() 方法获取锁,在没有获取到锁的阻塞过程中可以被打断lock.lockInterruptibly();// ...}catch (Exception e) {e.printStackTrace();}finally {lock.unlock();}System.out.println(Thread.currentThread().getName() + " 线程结束了");}public static void main(String[] args) {// 1、创建当前类对象Demo07LockInterruptibly demo = new Demo07LockInterruptibly();// 2、创建占用锁的线程(小强)new Thread(()->{demo.useLock();}, "thread-qiang").start();Thread thread = new Thread(() -> {demo.waitLock();}, "thread-ming");thread.start();try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {}// 打断小明线程的阻塞状态thread.interrupt();}
}

lockInterruptibly()模式下,被打断的线程,如果希望在被打断之后继续执行某些逻辑,那么可以在catch块编写。

3.4 读写锁

3.4.1 读写锁介绍

概念

在实际场景中,读操作不会改变数据,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。

针对这种场景,Java 的并发包提供了读写锁 ReentrantReadWriteLock,它表示两个锁,一个是读操作相关的锁,称为读锁,这是一种共享锁;一个是写相关的锁,称为写锁,这是一种排他锁,也叫独占锁、互斥锁。

进入条件

进入读锁条件:

  • 同一个线程内(可重入性角度):
    • 目前无锁:可以进入
    • 已经有读锁:可以进入
    • 已经有写锁:可以进入(锁可以降级,权限可以收缩)
  • 不同线程之间(排他性角度):
    • 其他线程已经加了读锁:可以进入
    • 其他线程已经加了写锁:不能进入

进入写锁条件:

  • 同一个线程内(可重入性角度):
    • 目前无锁:可以进入
    • 已经有读锁:不能进入(锁不能升级,权限不能扩大)
    • 已经有写锁:可以进入
  • 不同线程之间(排他性角度):
    • 其他线程已经加了读锁:不能进入
    • 其他线程已经加了写锁:不能进入
重要特征

公平选择性:

支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。

重进入:

读锁和写锁都支持线程重进入:

  • 同一个线程:加读锁后再加读锁
  • 同一个线程:加写锁后再加写锁

锁降级:

在同一个线程内:读锁不能升级为写锁,但是写锁可以降级为读锁。

3.4.2 ReadWriteLock接口

全类名:java.util.concurrent.locks.ReadWriteLock

源码如下:

public interface ReadWriteLock {/*** Returns the lock used for reading.** @return the lock used for reading.*/Lock readLock();/*** Returns the lock used for writing.** @return the lock used for writing.*/Lock writeLock();
}

readLock() 方法用来获取读锁,writeLock() 方法用来获取写锁。也就是说将文件的读写操作分开,分成两种不同的锁来分配给线程,从而使得多个线程可以同时进行读操作。

该接口下我们常用的实现类是:java.util.concurrent.locks.ReentrantReadWriteLock

3.4.3 ReentrantReadWriteLock类结构

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {/** 读锁 */private final ReentrantReadWriteLock.ReadLock readerLock;/** 写锁 */private final ReentrantReadWriteLock.WriteLock writerLock;final Sync sync;/** 使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock */public ReentrantReadWriteLock() {this(false);}/** 使用给定的公平策略创建一个新的 ReentrantReadWriteLock */public ReentrantReadWriteLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();readerLock = new ReadLock(this);writerLock = new WriteLock(this);}/** 返回用于写入操作的锁 */public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }/** 返回用于读取操作的锁 */public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }abstract static class Sync extends AbstractQueuedSynchronizer {}static final class NonfairSync extends Sync {}static final class FairSync extends Sync {}public static class ReadLock implements Lock, java.io.Serializable {}public static class WriteLock implements Lock, java.io.Serializable {}
}
总体结构图

在这里插入图片描述

3.4.4 典型案例

使用 ReentrantReadWriteLock 进行读和写操作

操作测试目标
场景一多个线程:同时获取读锁读锁可以共享
场景二多个线程:获取写锁写锁不能共享
场景三多个线程:一个线程先获取读锁后其他线程获取写锁读排斥写
场景四多个线程:一个线程获取写锁后其他线程获取读锁写排斥读
场景五同一个线程:获取读锁后再去获取写锁读权限不能升级为写权限
场景六同一个线程:获取写锁后再去获取读锁写权限可以降级为读权限
场景七同一个线程:获取读锁之后再去获取读锁读锁可重入
场景八同一个线程:获取写锁之后再去所获写锁写锁可重入

3.5 线程间通信

3.5.1 核心语法

  • ReentrantLock 同步锁:将执行操作的代码块设置为同步操作,提供原子性保证;
  • Condition 对象:对指定线程进行等待、唤醒操作;
    • await() 方法:让线程等待;
    • signal() 方法:将线程唤醒;
    • signalAll()方法:唤醒全部等待中的线程;

3.5.2 案例演示

代码实现
public class Demo03LockConditionWay {// 创建同步锁对象private Lock lock = new ReentrantLock();// 通过同步锁对象创建控制线程间通信的条件对象private Condition condition = lock.newCondition();private int data = 0;// 声明方法执行 + 1 操作public void doIncr() {try {// 使用 lock 锁对象加锁lock.lock();// 为了避免虚假唤醒问题:使用 while 结构进行循环判断// 判断当前线程是否满足执行核心操作的条件while (data == 1) {// 满足条件时,不该当前线程干活,所以进入等待状态condition.await();}// 不满足上面的条件时,说明该当前线程干活了,所以执行核心操作System.out.println(Thread.currentThread().getName() + " 执行 + 1 操作,data = " + ++data);// 自己的任务完成后,叫醒其它线程condition.signalAll();} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放锁lock.unlock();}}// 声明方法执行 - 1 操作public void doDecr() {try {// 使用 lock 锁对象加锁lock.lock();// 为了避免虚假唤醒问题:使用 while 结构进行循环判断// 判断当前线程是否满足执行核心操作的条件while (data == 0) {// 满足条件时,不该当前线程干活,所以进入等待状态condition.await();}// 不满足上面的条件时,说明该当前线程干活了,所以执行核心操作System.out.println(Thread.currentThread().getName() + " 执行 - 1 操作,data = " + --data);// 自己的任务完成后,叫醒其它线程condition.signalAll();} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放锁lock.unlock();}}public static void main(String[] args) {// 1、创建当前类的对象Demo03LockConditionWay demo = new Demo03LockConditionWay();// 2、创建四个线程,两个 + 1,两个 - 1new Thread(() -> {for (int i = 0; i < 20; i++) {demo.doIncr();}}, "thread-add A").start();new Thread(() -> {for (int i = 0; i < 20; i++) {demo.doDecr();}}, "thread-sub A").start();new Thread(() -> {for (int i = 0; i < 20; i++) {demo.doIncr();}}, "thread-add B").start();new Thread(() -> {for (int i = 0; i < 20; i++) {demo.doDecr();}}, "thread-sub B").start();}}

3.5.3 定制化通信

传统的 synchronized、wait()、notifyAll() 方式无法唤醒一个指定的线程。而 Lock 配合 Condition 的方式能够唤醒指定的线程,从而执行指定线程中指定的任务。

语法基础
  • ReentrantLock 同步锁:将执行操作的代码块设置为同步操作,提供原子性保证;
  • Condition 对象:对指定线程进行等待、唤醒操作;
    • await() 方法:让线程等待;
    • signal() 方法:将线程唤醒;
案例

要求:

要求四个线程交替执行打印如下内容:

  • 线程1:打印连续数字
  • 线程2:打印连续字母
  • 线程3:打印 * 符
  • 线程4:打印 $ 符

代码实现:

public class Demo03Condition {// 控制总体的操作步骤private int step = 1;// 负责打印数字的线程要打印的数字private int digital = 1;// 负责打印字母的线程要打印的字母private char alphaBet = 'a';// 同步锁对象private Lock lock = new ReentrantLock();// 条件对象:对应打印数字的线程private Condition conditionDigital = lock.newCondition();// 条件对象:对应打印字母的线程private Condition conditionAlphaBet = lock.newCondition();// 条件对象:对应打印星号的线程private Condition conditionStar = lock.newCondition();// 条件对象:对应打印 $ 的线程private Condition conditionDollar = lock.newCondition();// 声明一个方法专门打印数字public void printDigital() {try {lock.lock();// 只要 step 对 4 取模不等于 1,就不该当前方法干活while (step % 4 != 1) {// 使用专门的条件对象,让当前线程进入等待// 将来还用同一个条件对象,调用 singal() 方法就能精确的把这里等待的线程唤醒conditionDigital.await();}// 执行要打印的操作System.out.print(digital++);// 精准唤醒打印字母的线程conditionAlphaBet.signal();step++ ;} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void printAlphaBet() {try {lock.lock();while (step % 4 != 2) {conditionAlphaBet.await();}System.out.print(alphaBet++);conditionStar.signal();step++ ;} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void printStar() {try {lock.lock();while (step % 4 != 3) {conditionStar.await();}System.out.print("*");conditionDollar.signal();step++ ;} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void printDollar() {try {lock.lock();while (step % 4 != 0) {conditionDollar.await();}System.out.println("$");conditionDigital.signal();step++ ;} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public static void main(String[] args) {Demo03Condition demo = new Demo03Condition();new Thread(()->{for (int i = 0; i < 10; i++) {demo.printDigital();}}).start();new Thread(()->{for (int i = 0; i < 10; i++) {demo.printAlphaBet();}}).start();new Thread(()->{for (int i = 0; i < 10; i++) {demo.printStar();}}).start();new Thread(()->{for (int i = 0; i < 10; i++) {demo.printDollar();}}).start();}}

3.6 Lock与synchronized对比

结论:在实际开发时,如果synchronized能够满足需要,那就使用synchronized,毕竟它自动加锁、解锁,代码简单。 如果synchronized无法满足需求,只能使用Lock。

3.6.1 相同点

  • 都支持独占锁

  • 都支持可重入

3.6.2 不同点

Lock 系列 API 用法synchronized 用法
加锁/解锁手动自动
支持共享锁×
支持尝试获取锁失败
后执行特定操作
×
灵活×
便捷×
响应中断lockInterruptibly()
方式支持阻塞状态响应中断
sleep()
睡眠后支持响应中断

被synchronized阻塞
不支持响应中断
代码风格面向对象面向过程
底层机制AQS(volatile + CAS + 线程的双向链表)= 非阻塞同步阻塞同步

3.6.3 使用建议

从功能效果的角度

Lock 能够覆盖 synchronized 的功能,而且功能更强大。

在这里插入图片描述

从开发便捷性的角度
  • synchronized:自动加锁、解锁,使用方便
  • Lock:手动加锁、解锁,使用不那么方便
从性能角度

二者差不多;

使用建议

synchronized 够用,那就使用 synchronized;如果需要额外附加功能则使用 Lock:

  • 公平锁
  • 共享锁
  • 尝试获取锁
  • 以支持响应中断的方式获取锁
  • ……

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

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

相关文章

【JavaScript算法】DOM树层级显示

题目描述&#xff1a; 上述表达式的输出结果为 [DIV] [P, SPAN, P, SPAN] [SPAN, SPAN]直接上代码 let tree document.querySelector(".a"); function traverseElRoot(elRoot) {const result [];function traverse(element, level) {if (!result[level]) {resul…

ASR-LLM-TTS 大模型对话实现案例;语音识别、大模型对话、声音生成

参考:https://blog.csdn.net/weixin_42357472/article/details/136305123(llm+tts) https://blog.csdn.net/weixin_42357472/article/details/136411769 (asr+vad) 这里LLM用的是chatglm;电脑声音播报用的playsound 1、实时语音识别版本 注意:暂时这项目有个缺陷就是tts…

HarmonyOS 应用开发之UIAbility组件启动模式

UIAbility的启动模式是指UIAbility实例在启动时的不同呈现状态。针对不同的业务场景&#xff0c;系统提供了三种启动模式&#xff1a; singleton&#xff08;单实例模式&#xff09;multiton&#xff08;多实例模式&#xff09;specified&#xff08;指定实例模式&#xff09;…

成都市酷客焕学新媒体科技有限公司:实现品牌的更大价值!

成都市酷客焕学新媒体科技有限公司专注于短视频营销&#xff0c;深知短视频在社交媒体中的巨大影响力。该公司巧妙地将品牌信息融入富有创意和趣味性的内容中&#xff0c;使观众在轻松愉悦的氛围中接受并传播这些信息。凭借独特的创意和精准的营销策略&#xff0c;成都市酷客焕…

5、axios请求、动画、组件、路由重定向、UI组件

一、axios请求 Axios是一个基于Promise的HTTP状态库&#xff0c;封装ajax。ajax包含axios安装 npm install axios 引入 import axios form “axios” 1、get请求 <script> // 1.本页面引入 import axios from "axios";data() {return {imgSrc: ""…

pygame用chatgpt绘制3d沿x轴旋转的

import pygame from pygame.locals import * import sys import mathpygame.init()width, height 800, 600 screen pygame.display.set_mode((width, height))vertices [(0, 100, 0), (100, 200, 0), (300, 100, 0)]angle 0 rotation_speed 2 # 可根据需要调整旋转速度 c…

数据结构-树-006

1二叉树 1.1目标二叉树 前序遍历&#xff1a;ABDHIEJCFKG 中序遍历&#xff1a;HDIBEJAFKCG 后序遍历&#xff1a;HIDJEBKFGCA 层序遍历&#xff1a;ABCDEFGHIJK运行结果&#xff1a; 运行结果符合目标二叉树的深度优先&#xff08;前序遍历&#xff0c;中序遍历&#xff0c;…

leetcode216组合总和III

本题思考&#xff1a; 对于输入样例k3,n9 输出里面为什么只有 [[1,2,6],[1,3,5],[2,3,4]]而没有下图所示的重复情况出现呢&#xff1f; 当时代码写错了&#xff0c;思考许久不得解&#xff0c;后面经过仔细对比代码之后发现是我的代码出现了逻辑错误&#xff0c;而正是这一关键…

基于SSM的高校普法系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的高校普法系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring Spri…

Python与人工智能:气象领域的数据处理与模型优化

Python是功能强大、免费、开源&#xff0c;实现面向对象的编程语言&#xff0c;在数据处理、科学计算、数学建模、数据挖掘和数据可视化方面具备优异的性能&#xff0c;这些优势使得Python在气象、海洋、地理、气候、水文和生态等地学领域的科研和工程项目中得到广泛应用。可以…

九河云荣获“华为2024·亚太区年度杰出合作伙伴奖”

2024年3月26日~27日&#xff0c;以“加速智能化&#xff0c;一切皆服务”为主题的华为亚太生态伙伴大会在东莞隆重开幕&#xff0c;九河云作为专业的多云管理服务商&#xff0c;凭借多年来在云领域的赋能发展应邀出席并荣获“亚太区年度杰出伙伴奖”&#xff0c;这不仅彰显了九…

关系型数据库mysql(6)备份与恢复

一.数据备份的重要性 &#xff08;1&#xff09;在生产环境中&#xff0c;数据的安全性至关重要 &#xff08;2&#xff09;任何数据的丢失都可能产生严重的后果 &#xff08;3&#xff09;造成数据丢失的原因 程序错误人为操作失误运算错误磁盘故障灾难&#xff08;如火灾…

【HCIP学习】网络类型级数据链路层协议

思维导图在上面哦~ 一、网络类型的分类&#xff08;4种&#xff09; 出现原因&#xff1a;数据链路层使用的协议及规则不同&#xff0c;造成了不同的网络类型 1、多点接入网络&#xff08;MA&#xff09;------一条网段内上出现多个设备 BMA&#xff1a;广播型多点接入&…

第十三届蓝桥杯省赛真题 Java 研究生 组【原卷】

文章目录 发现宝藏【考生须知】试题 A: 排列字母试题 B: 灭鼠先锋试题 C: 质因数个数试题 D: 数位排序试题 E: 蜂巢试题 F : \mathrm{F}: F: 爬树的甲壳虫试题 G: 重新排序试题 H \mathrm{H} H : 技能升级试题 I: 最优清零方案试题 J : \mathrm{J}: J: 推导部分和 发现宝藏 …

ctf.show_web10

本题重点&#xff1a;&#xff08;1&#xff09;php相关函数 &#xff1b;&#xff08;2&#xff09;会灵活运用 group by 和 with rollup&#xff1b; <?php $flag""; function replaceSpecialChar($strParam){ $regex "/(s…

Mybatis-各种查询功能

比较推荐的是用para注解&#xff0c;我们一步一步讲 1. 查询一个实体类对象 若查询出的数据只有一条&#xff0c;可以通过实体类对象接收查询结果 查询一个实体类对象&#xff0c;根据id查询 SelectMapper.java package com.sakurapaid.mybatis3.select.mapper;import com.s…

Redis 特性,为什么要用Redis,Redis到底是多线程还是单线程

一、Redis介绍 Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务&#xff0c;是一个开源的&#xff0c;使用C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 二、特性(为什么要用Redis&#x…

NFT Insider #125:Astar将与索尼开发的新公链将关注游戏或 NFT 等众多领域

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members &#xff08;https://twitter.com/WHALEMembers&#xff09;、BeepCrypto &#xff08;https://twitter.com/beep_crypto&#xff09;联合出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜…

Python 多线程同步锁实战

大家好&#xff0c;今天我们要聊聊Python中的多线程世界&#xff0c;你知道吗&#xff1f;在并行处理任务时&#xff0c;多线程就像厨房里的大厨们同时烹饪多个菜品&#xff0c;但得保证每道菜都能完美出锅。这就需要我们引入一个神秘的角色——同步锁&#xff08;Lock&#xf…

docker容器内存检测排查

查询容器使用内存 在运维当中&#xff0c;你会发现内存很彪的高&#xff0c;但是如何判断为什么会高&#xff0c;是什么样的程序造成的呢&#xff1f;赶快使用 top&#xff0c;或者 free -h或者 ps -v。是吗&#xff1f;道理是对的。 但是你会发现&#xff0c;全部都是docker…