多线程初阶(三)- 线程案例

目录

1.单例模式

(1)饿汉模式

(2)懒汉模式

前言

懒汉式1-synchronized加锁

懒汉式2-双重if保障

懒汉式3-volatile防止误判 

2.生产者消费者模型

(1)阻塞队列

 (2)优点

解耦合

削峰填谷

(3)代价

(4)代码案例 

模拟实现阻塞队列

3.线程池

 (1)操作系统的两种状态

内核态(Kernel Mode)

用户态(User Mode)

(2)实现线程池

(3)Java标准库提供的线程池

 构造方法

注意事项 

(4)线程池优点

4.定时器

(1)标准库中的定时器

(2)实现定时器


1.单例模式

设计模式:即针对某些特定的场景,大佬们设计出来的一些固定套路来供我们使用

单例模式能保证某个类在程序中只存在唯一一份实例,而不会创建出多个实例.

这一点在很多场景上都需要.比如JDBC中的DataSource实例就只需要一个.


单例模式具体的实现方式有很多.最常见的是"饿汉"和"懒汉"两种.
单例模式前提是"一个进程中",如果是有多个Java进程,自然每个进程中都可以有一个实例了

(1)饿汉模式

“饿汉”二字我们可以理解为迫切的意思,在类加载的时候就会创建这个单例的实例 (单个对象)

class Singleton{private static final Singleton instance = new Singleton();private Singleton() {};public static Singleton getInstance() {return instance;}
}public class Demo1 {public static void main(String[] args) {Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2);}
}
  • static修饰instance成员变量就是“类成员”,类成员的初始化就是在Singleton这个类被加载的时候(程序启动的时候);它属于类本身,而不是类的某个特定对象。因此,这个实例变量在内存中只有一份拷贝,被类的所有实例共享

    当Singleton类被JVM加载到内存中时,就会创建一个Singleton的实例,并将其赋值给instance变量。由于instance是static的,所以无论创建多少个Singleton的实例,instance都指向同一个对象
  • 我们这里创建的getInstance方法就是为了保证每次访问Singleton类时要通过该方法来获取实例,避免被人new对象,这也是单例模式解决的主要问题
  • private Singleton() {};这个构造方法意味着在类的外面,就无法调用构造方法,无法初始化也就无法创建实例了

(2)懒汉模式

前言

首次调用getInstance为null时就会进入if语句来实例化对象,之后再调用getInstance时就不再重新创建直接返回

class SingletonLazy{private static SingletonLazy instance = null;private SingletonLazy() {};public static SingletonLazy getInstance() {if (instance == null) {instance = new SingletonLazy();}return instance;}
}

这样看似乎没有什么问题,当我们将它与多线程结合起来就会存在线程安全问题

这里的if语句和下面的实例化赋值修改的语句就会存在线程安全问题,这两个语句之间可能会存在线程切换的问题

无论new操作是在本线程,还是其他线程进行的此处的 return 的值都是Instance内存中的最新值

 懒汉式1-synchronized加锁

如何解决呢?进行加锁操作,安全性问题时发生在if判断语句和内部的赋值语句的,在这之间可能会发生线程的切换,所以我们需要将这两步具有原子性,对其进行加锁 

class SingletonLazy{private static SingletonLazy instance = null;private static Object locker = new Object();private SingletonLazy() {};public static SingletonLazy getInstance() {synchronized (locker) {if (instance == null) {instance = new SingletonLazy();}}return instance;}
}

加锁之后便解决了线程安全问题,但与此同时也可能带来阻塞的问题,在我们new操作执行过一次后,if语句就不再发挥作用,之后都是直接返回该对象,后续的代码就只是单纯的读操作,此时之后的每次加锁就是无用功,每次都会触发加锁的操作,虽然之后不会发生线程安全问题,但却因为加锁这一步骤产生了阻塞进而影响到了性能

  懒汉式2-双重if保障

因此我们需要再次进行判断,当第一次调用getInstance方法时,instance为null,此时就需要加锁,当之后再访问时如果instance为空就在进入判断,不为空就不再进入

synchronized会使代码出现阻塞,这一旦阻塞之后,啥时候恢复执行,中间可能是"沧海桑田"在这个过程中,很可能其他线程就把这个值给修改了
因为是多线程,两行代码之间可能会穿插其他代码

class SingletonLazy{private static SingletonLazy instance = null;private static Object locker = new Object();private SingletonLazy() {};public static SingletonLazy getInstance() {if (instance == null) {synchronized (locker) {if (instance == null) {instance = new SingletonLazy();}}}return instance;}
}
  • 外面的if语句是判断是否需要加锁
  • 里面的if语句是判断是否需要创建对象

懒汉式3-volatile防止误判 

此时依然存在问题,我们上篇文章说过开发编译器的大佬们为了提高大家的效率会进行优化,这里就进行了指令重排序,从而引起了线程安全问题,此时为了防止误判,我们依旧加volatile关键字进行标记即可

代码中的 instance = new SingletonLazy();可分为三步

  1. 分配内存空间
  2. 执行构造方法
  3. 内存空间的地址赋值给引用变量

编译器会先执行第一步,2和3谁先执行不确定

对于单线程来说,先执行2还是先执行3,本质上是一样的


这里我们可以这样理解,这三步我们可以看作

  1. 买房
  2. 装修
  3. 收房拿钥匙

对应顺序123步骤则拿到的就是精装房(已经装修好了),而132则是毛坯房(之后自己装修)

class SingletonLazy{private static volatile SingletonLazy instance = null;private static Object locker = new Object();private SingletonLazy() {};public static SingletonLazy getInstance() {if (instance == null) {synchronized (locker) {if (instance == null) {instance = new SingletonLazy();}}}return instance;}
}

2.生产者消费者模型

(1)阻塞队列

阻塞队列是一种特殊的队列,也遵循“先进先出”的原则

阻塞队列能是一种线程安全的数据结构,(标准库中原有的队列Queue和其子类默认都是线程不安全的)并且具有以下特性:

  • 当队列满的时候,继续入队列就会阻塞,直到有其它线程从队列中取走元素
  • 当队列空的时候,继续出队列也会阻塞,直到有其它线程往队列中插入元素

阻塞队列的一个典型的应用场景就是“生产者消费者模型”,这是一种典型的开发模型

生活场景举例:

服务器之间的通信举例:

我们当下的分布式系统也是如此,并不是一个系统解决所有问题,而是多个系统之间相互调用

 (2)优点

解耦合

  1. 服务器之间的“解耦合”(理想状态下希望服模块之间的关联程度/影响程度较低,也就是低耦合)

    此时服务器之间的耦合性就较高,若一个服务器挂了,与其相连的服务器也会是受到较大的影响,所以此时我们就需要引入阻塞队列使用生产者消费者模型来降低耦合

    此时AB之间通过阻塞队列,达到了解耦合的效果,但是A和队列,B和队列之间是否又引入了新的耦合呢?答案是没有,我们通常所谈到的阻塞队列是代码中的一个数据结构,由于这个东西太好用,以至于会把这样的数据结构单独封装成一个服务器程序并且在单独的服务器机器上进行部署,此时这样的阻塞队列就叫做“消息队列”(Message Queue,MQ),消息队列是成熟的产品,代码不会频繁修改,代码是稳定的,所以相互之间的逻辑基本一次就顶下来了

 削峰填谷

 通过中间的阻塞队列,可以起到“削峰填谷”的效果,在遇到请求量激增的情况下可以保护下游服务器不会被请求冲垮
消息队列(阻塞队列)服务器通信过程中,也是能起到的削峰填谷

为什么一个服务器收到的请求越多,就可能会挂?

一台服务器就类似于是一台电脑,上面就提供了一些硬件资源(CPU,内存,硬盘,网络带宽等),尽管配置再好,硬件资源也是有限,当服务器每次收到一个请求,处理这个请求的过程中就都需要执行一系列的代码,在执行这些代码的过程中就会需要消耗一定的硬件资源,当这些请求消耗的总的硬件资源超过了机器能提供的上限,那么此时机器就会出现问题(卡死,程序崩溃等)

  1. 资源耗尽:内存耗尽,CPU过载,磁盘IO瓶颈(在处理请求时,服务器可能需要频繁地进行磁盘读写操作。如果请求量过大,磁盘IO会成为瓶颈,导致请求处理速度下降,甚至导致服务器崩溃)
  2. 并发连接数有限(每个服务器的并发连接数都是有限的)
  3. 资源争夺(请求量过大,资源争夺会变得更加激烈,导致处理请求的时间变长,进而影响服务器的稳定性和性能)
  4. 设计局限(有的服务器不具备高并发能力)
  5. 外部因素(网络不稳定,温度过高等)

在请求激增的时候,A为啥不会挂?队列为啥不会挂?反而是B更容易挂呢??

A的角色是一个“网关服务器”,收到客户端的请求再把请求转发给其他服务器,这样服务器里面的代码做的工作比较简单(单纯的数据转发)消耗的硬件资源通常更少,因此在处理同一个请求时,消耗的资源更少,同样的配置下就能支持更多的请求处理,

同理,队列其实也是比较简单的程序,单位请求消耗的硬件资源也是比较少的

B这个服务器是真正工作的服务器,要真正完成一系列的业务逻辑,这一系列的工作代码量非常大,消耗的时间很多,消耗的硬件资源也是更多的

(3)代价

  1. 硬件成本增加
  2. 通信时间变长

(4)代码案例 

public class Demo3 {public static void main(String[] args) throws InterruptedException {//阻塞队列的容积为3BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(3);queue.put(111);System.out.println("put成功");queue.put(222);System.out.println("put成功");queue.put(333);System.out.println("put成功");//阻塞队列的容量为3,当添加第4个数据时,就会发生阻塞等待queue.put(333);System.out.println("put成功");}
}

模拟实现阻塞队列

class MyBlockingQueue{public String[]arr = null;public int first;public int last;public MyBlockingQueue(int capacity) {arr = new String[capacity];}public int size;public void put(String s) throws InterruptedException {synchronized (this) {if (size == arr.length) {this.wait();}arr[size] = s;if (size > arr.length) {last = 0;}size++;this.notify();}}public String take() throws InterruptedException {String ret = "";synchronized (this) {if (size == 0) {this.wait();}ret = arr[first];first++;if (first >= arr.length) {first = 0;}size--;this.notify();}return ret;}
}

 我们通过观察wait()方法的源代码发现源码建议我们将wait()方法 放在while循环中使用

原因:因为wait并非只能被notify来唤醒,还可能被interrupt方法以打断的方式来唤醒
while的作用就是在wait被唤醒之后再次确认条件,看是否能继续执行


场景:实现一个充值的逻辑,某个线程在阻塞等待,队列里玩家的充值数据,一旦充值数据到账就把对应的道具发放给玩家,可能会出现玩家可能正要充值(还没充)因为interrrupt的不小心操作,导致队列里读取出一个“错误值”
 

public class Demo5 {private static Object locker = new Object();public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{synchronized (locker) {System.out.println("wait之前");try {locker.wait();} catch (InterruptedException e) {System.out.println("wait之后");e.printStackTrace();}}});Thread.sleep(1000);t.start();t.interrupt();}
}

Interrupt是能够唤醒阻塞的方法.sleep (wait和sleep 都是类似的阻塞的方法)
lnterrupt本来的目的是,让线程终止.唤醒阻塞,只是"终止线程的一个环节"

3.线程池

“池”这种思想本质上就是为了提高程序的效率,我们之前有学过字符串常量池,如果字符串常量池中已经存在相同的字符串,则不会重复创建新的字符串对象,而是直接返回池中已有的字符串对象的引用。

此处的线程池也是类似的思想,最初引入线程的原因就是进程太重了,频繁的销毁进程开销会很大,(只是相比于线程来说开销很大),但随着业务对于性能的要求越来越高,对应的线程创建/销毁的频次就会越来越多,与此同时线程大批量的开销也会变得越来越大,此时就无法不忽略不计了;而线程池就是解决该问题的常见方案
线程池就是把线程提前从系统当中申请好放到一个地方,当后续需要使用线程的时候直接从这个地方来取,而不是从系统中重新申请,当线程用完之后也是还回到刚才这个地方

 (1)操作系统的两种状态

内核态(Kernel Mode)

定义与特点

  1. 内核态是操作系统运行的一种高级别状态,也被称为内核空间或特权模式。
  2. 在内核态下,CPU可以执行任何指令,访问任意的数据,包括外围设备(如网卡、硬盘等),并且可以从一个程序切换到另一个程序,占用CPU时不会发生抢占情况。
  3. 内核态主要负责运行系统、硬件交互,以及提供稳定的环境供应用程序运行。
  4. 内核态运行的代码不受任何限制,具有最高的权限级别,通常被操作系统本身及其相关模块所使用。

功能与作用

  1. 控制计算机的硬件资源,如协调CPU资源、分配内存资源等。
  2. 提供系统调用接口,允许用户态的程序通过系统调用来访问内核管理的资源。
  3. 实现保护机制,防止用户进程误操作或恶意破坏系统。

用户态(User Mode)

定义与特点

  • 用户态是操作系统运行的另一种状态,也被称为用户空间或非特权模式。
  • 在用户态下,CPU只能访问受限的资源,不能直接访问内存等硬件设备,必须通过系统调用陷入到内核中才能访问这些特权资源。
  • 用户态主要用于执行用户程序,提供应用程序运行的空间。
  • 用户态运行的代码需要受到CPU的很多检查,不能直接访问内核数据和程序。

功能与作用

  • 执行用户编写的应用程序代码。
  • 通过系统调用请求内核态的服务,如文件操作、网络通信等。
  • 在用户态下,每个进程都在各自的用户空间中运行,不允许存取其他程序的用户空间,从而保证了系统的安全性和稳定性。

操作系统是由操作系统的内核和操作系统配套的应用程序组成的,内核是操作系统的核心功能部分,负责完成一个操作系统的核心工作,对应的执行很多代码的逻辑都是要用户态的代码和内核态的代码配合完成的(用户态调用内核的api),多种的应用程序也都是由内核统一负责管理和服务的,内核里的工作就可能时非常繁忙的,也就是说用户态提交给内核要做的任务是不可控的(内核可能还会穿插着做其它事)
 

  • 从系统创建线程,这样的逻辑就是调用系统api,由系统内核执行一系列逻辑来完成这个过程
  • 直接从线程中取,就相当于整个过程都是纯用户态的代码,是自己可控的,因此更高效

所以我们认为纯用户态的操作比经过内核的操作效率更高

(2)实现线程池

class MyThreadPool {private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();// 通过这个⽅法, 来把任务添加到线程池中. public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);}// n 表⽰线程池⾥有⼏个线程. // 创建了⼀个固定数量的线程池. public MyThreadPool(int n) {for (int i = 0; i < n; i++) {Thread t = new Thread(() -> {while (true) {try {// 取出任务, 并执⾏Runnable runnable = queue.take();runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}}
}public class Demo {public static void main(String[] args) throws InterruptedException {MyThreadPool pool = new MyThreadPool(4);for (int i = 0; i < 1000; i++) {pool.submit(new Runnable() {@Overridepublic void run() {// 要执⾏的⼯作 System.out.println(Thread.currentThread().getName() + " hello"}});}}
}

(3)Java标准库提供的线程池

 构造方法

注意事项 

Java标准库的线程池中把里面的线程分成两类:

  • 核心线程(可理解为最少有多少个线程)
  • 非核心线程(线程扩容中新增的)

核心线程数+非核心线程数的最大值就是最大线程数
核心线程会始终存在于线程池内部,非核心线程繁忙时会被创建出来,不繁忙空闲时就会把这些线程真正释放掉


BlockingQueue<Runnable> workQueue表示工作队列,线程池的工作过程是典型的“生产者消费者模型”,我们在使用时通过形容submit这样的方法把要执行的任务(Runnable)设定到线程池里,线程池内部的工作线程就负责执行这些任务

我们知道构造方法是一个特殊的方法,必须和类名一样,多个版本的构造方法必须是通过“重载”,那我们就可能会遇到以下情况


四种拒绝策略

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Demo1 {public static void main(String[] args) {ExecutorService service = Executors.newFixedThreadPool(4);for (int i = 0; i < 100; i++) {int id = i;service.submit(()-> {Thread current = Thread.currentThread();System.out.println(id + current.getName());});}}
}

执行这个代码虽然100个任务都执行完毕了,但是整个进程并没有结束,是因为此处的线程池创建出来的线程,默认都是“前台线程”,虽然main线程结束了,但是这些线程池里的前台线程仍然存在

(4)线程池优点

  1. 降低资源消耗:减少线程的创建和销毁带来的性能开销。
  2. 提高响应速度:当任务来时可以直接使用,不用等待线程创建
  3. 可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象。

4.定时器

定时器也是软件开发中的⼀个重要组件.类似于一个"闹钟".达到⼀个设定的时间之后,就执行某个指定 好的代码.

定时器是一种实际开发中非常常用的组件.
比如网络通信中,如果对方500ms内没有返回数据,则断开连接尝试重连.比如一个Map,希望里面的某个key在3s之后过期(自动删除).
类似于这样的场景就需要用到定时器.

(1)标准库中的定时器

  •  标准库中提供了⼀个Timer类.Timer类的核心方法为schedule .
  •  schedule 包含两个参数.第一个参数指定即将要执行的任务代码,第二个参数指定多长时间之后 执行(单位为毫秒).

Timer构造方法:

schedule方法:

import java.util.Timer;
import java.util.TimerTask;public class Demo2 {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("1");}},3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("2");}},2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("3");}},1000);System.out.println("程序开始运行");}
}

(2)实现定时器

  1. 创建类,描述一个要执行的任务是啥.
    (任务的内容,任务的时间)
  2. 管理多个任务通过一定的数据结构,把多个任务存起来.
  3. 有专门的线程,执行这里的任务



考虑线程安全问题:

import java.util.PriorityQueue;
import java.util.Timer;class TimerTask implements Comparable<TimerTask>{private Runnable runnable;private long time;public TimerTask(Runnable runnable,long delay) {this.runnable = runnable;this.time = System.currentTimeMillis() - delay;}public void run() {runnable.run();}public long getTime() {return time;}@Overridepublic int compareTo(TimerTask o) {return (int) (this.time - o.time);}
}
class MyTimer {private Object locker = new Object();private PriorityQueue<TimerTask> queue = new PriorityQueue<>();//要有专门的线程去执行TimerTaskpublic MyTimer() {Thread t = new Thread(() -> {try {while (true) {synchronized (locker) {while (queue.isEmpty()) {locker.wait();}TimerTask current = queue.peek();// 比如, 当前时间是 10:30, 任务时间是 12:00, 不应该执行.// 如果当前时间是 10:30, 任务时间是 10:29, 应该执行if (System.currentTimeMillis() >= current.getTime()) {// 要执行任务current.run();// 把执行过的任务, 从队列中删除.queue.poll();} else {// 先不执行任务locker.wait(current.getTime() - System.currentTimeMillis());// Thread.sleep(current.getTime() - System.currentTimeMillis());}}}} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();}public void schedule(Runnable runnable,long delay) {//添加任务synchronized (locker) {TimerTask timerTask = new TimerTask(runnable,delay);queue.offer(timerTask);locker.notify();}}}public class Demo3 {public static void main(String[] args) {MyTimer myTimer = new MyTimer();myTimer.schedule(() -> {System.out.println("hello 3000");}, 3000);myTimer.schedule(() -> {System.out.println("hello 2000");}, 2000);myTimer.schedule(() -> {System.out.println("hello 1000");}, 1000);System.out.println("程序开始执行");}
}

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

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

相关文章

鸿蒙OpenHarmony Native API【结构体】 头文件

OH_Drawing_BitmapFormat Overview Related Modules: [Drawing] Description: 结构体用于描述位图像素的格式&#xff0c;包括颜色类型和透明度类型 Since: 8 Version: 1.0 Summary Public Attributes Public Attribute NameDescriptioncolorFormatalphaFormat De…

PlantUML学习笔记-嵌入式系统设计常用图例

在嵌入式系统设计过程中&#xff0c;需要使用一些图例对系统框架及业务流程进行说明&#xff0c;以便于多人协同开发及后期的系统维护&#xff0c;提高团队开发效率。 1. 嵌入式设计开发常使用的图例&#xff1a; 1.1 用例图&#xff08;Use Case Diagram&#xff09; 用例图…

KamaCoder 99. 岛屿数量 + Leetcode 200. Number of Islands

99. 岛屿数量 题目描述&#xff1a; 给定一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的矩阵&#xff0c;你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成&#xff0c;并且四周都是水域。你可以假设矩阵外均被水包围。 输…

【LeetCode】栈 - 20.有效的括号、150.逆波兰表达式求值、155.最小栈、栈的压入、弹出序列

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构 &#x1f4da;本系列文章为个人学…

【前端/js】使用js读取本地文件(xml、二进制)内容

目录 说在前面FileReaderDOMParser文本文件二进制文件 说在前面 浏览器版本&#xff1a;Microsoft Edge 126.0.2 (正式版本) (64 位) FileReader MDNFileReader 接口允许 Web 应用程序异步读取存储在用户计算机上的文件&#xff08;或原始数据缓冲区&#xff09;的内容&#x…

分布式光伏并网AM5SE-IS防孤岛保护装置介绍——安科瑞 叶西平

产品简介 功能&#xff1a; AM5SE-IS防孤岛保护装置主要适用于35kV、10kV及低压380V光伏发电、燃气发电等新能源并网供电系统。当发生孤岛现象时&#xff0c;可以快速切除并网点&#xff0c;使本站与电网侧快速脱离&#xff0c;保证整个电站和相关维护人员的生命安全。 应用…

模拟实现c++中的vector模版

目录 一vector简述&#xff1a; 二vector的一些接口函数&#xff1a; 1初始化&#xff1a; 2.vector增长&#xff1a; 3vector增删查改&#xff1a; 三vector模拟实现部分主要函数&#xff1a; 1.size,capacity,empty,clear接口&#xff1a; 2.reverse的实现&#xff1…

Golang | Leetcode Golang题解之第292题Nim游戏

题目&#xff1a; 题解&#xff1a; func canWinNim(n int) bool {return n%4 ! 0 }

【一图学技术】SDK和API有什么关系?

API&#xff08;应用程序编程接口&#xff09;&#xff1a; API是一组定义了软件组件之间交互的规则和协议。 它定义了如何请求某个功能或服务&#xff0c;并指定了数据的格式和传输方式。API 可以用于不同的编程语言和平台。 API提供了一种标准化的方式&#xff0c;使不同的应…

[译] 深入浅出Rust基金会

本篇是对 RustConf 2023中的Rust Foundation: Demystified这一视频的翻译与整理, 过程中为符合中文惯用表达有适当删改, 版权归原作者所有. 大家好,我是Sage Griffin,我的代词是they/them。我今天来这里是要谈谈Rust基金会。 要了解基金会实际做什么,我们需要理解美国国内税收…

6.3 面向对象技术-设计模式

设计模式 创建型模式 结构型模式

布尔盲注——多种方式实现及利用burpsuite爆破

目录 1、判断闭合符类型 2、爆数据库长度 3、查询库名 手动注入 burpsuite爆破 方法一&#xff1a;用ASCII码值转化爆破 方法二&#xff1a;left方法直接爆破字母 方法三&#xff1a;if方法爆破注入&#xff08;最简单&#xff09; 4、爆破表名 5、爆破具体值 当我们改变前端…

Java | Leetcode Java题解之第279题完全平方数

题目&#xff1a; 题解&#xff1a; class Solution {public int numSquares(int n) {if (isPerfectSquare(n)) {return 1;}if (checkAnswer4(n)) {return 4;}for (int i 1; i * i < n; i) {int j n - i * i;if (isPerfectSquare(j)) {return 2;}}return 3;}// 判断是否为…

夜不能寐?解锁失眠自救秘籍,让你重拾安睡之夜!

在这个快节奏的时代&#xff0c;失眠似乎成了许多人的“隐形伴侣”&#xff0c;悄悄侵蚀着我们的健康与幸福感。夜深人静&#xff0c;万籁俱寂之时&#xff0c;你却辗转反侧&#xff0c;难以入眠&#xff0c;第二天又拖着疲惫的身躯迎接新的挑战。别担心&#xff0c;今天我们就…

大脑自组织神经网络通俗讲解

大脑自组织神经网络的核心概念 大脑自组织神经网络&#xff0c;是指大脑中的神经元通过自组织的方式形成复杂的网络结构&#xff0c;从而实现信息的处理和存储。这一过程涉及到神经元的生长、连接和重塑&#xff0c;是大脑学习和记忆的基础。其核心公式涉及神经网络的权重更新…

二分法各种边界,大彻大悟

1要考虑四个角度的“边界”&#xff0c;如下图 2 先考虑角度a的第一种情况 如下图所示&#xff0c;对于左边的情况&#xff0c;因为当l3&#xff0c;r4的时候&#xff0c;mid等于3&#xff0c;已知target4, 如果lmid就陷入死循环&#xff0c;所以l。右边同理。 判断c1 c2的影…

热门音效、BGM哪里可以免费下载?

剪辑的奇妙世界等你探索&#xff01;在这个创意的领域里&#xff0c;音效是创造氛围、增强表现力的重要元素。我整理了8个优质的剪辑音效素材网站&#xff0c;它们提供了丰富多样的音效资源&#xff0c;无论是制作视频、音乐还是动画&#xff0c;都能为你提供所需的声音。 1、b…

大模型学习笔记十四:Agent模型微调

文章目录 一、大模型需要Agent技术的原因二、Prompt Engineering可以实现Agent吗&#xff1f;&#xff08;1&#xff09;ReAct原理展示和代码&#xff08;2&#xff09;ModelScope&#xff08;3&#xff09;AutoGPT&#xff08;4&#xff09;ToolLLaMA 三、既然AutoGPT可以满足…

LaTeX如何改变字体颜色

诸神缄默不语-个人CSDN博文目录 在LaTeX文档中&#xff0c;改变字体颜色是一个常见需求&#xff0c;尤其是在需要强调特定文本或使文档更加生动的时候。本文将介绍如何使用\color{}命令来更改字体颜色。 文章目录 基本用法示例代码预定义颜色使用自定义颜色总结本文撰写过程中…