当前位置: 首页 > news >正文

Java基础学习(16)多线程

Java基础学习多线程

  • 一、多线程
    • 1.1 什么是多线程
    • 1.2 多线程的两个概念
      • 1.2.1 并发
    • 1.3 多线程的实现方式
    • 1.4 多线程的成员方法
    • 1.5 线程的生命周期
  • 二、线程安全
    • 1.6 同步方法
    • 1.7 锁lock
    • 1.8 死锁
    • 1.8 生产者和消费者 (等待唤醒机制)
    • 1.9 等待唤醒机制(阻塞队列方式实现)
    • 1.10 线程状态
  • 二、线程池
    • 2.1 线程池的概述
    • 2.2 自定义线程池
    • 2.3 线程池到底多大才合适

一、多线程

1.1 什么是多线程

线程

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位.
应用软件中互相独立,可以同时运行的功能

进程:
进程是程序的基本执行实体

多线程的应用场景:

  1. 软件中的耗时操作
  2. 拷贝、迁移大文件
  3. 加载大量的资源文件
  4. 所有的聊天软件
  5. 所有的后台服务器
    ……
    同时这些多项任务,提升运行效率

1.2 多线程的两个概念

1.2.1 并发

并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行
在这里插入图片描述

1.3 多线程的实现方式

  • 继承Thread类的方式进行实现

多线程的第一种启动方式:

  1. 自己定义一个类继承Thread
  2. 重写run方法
  3. 创建子类的对象,并启动线程

重写的Run方法:

package MyThreads;
//重写run方法(Thread)
public class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + "HelloWorld!!");}}
}

启动的线程:

package MyThreads;
//新建Thread
public class Dom1 {public static void main(String[] args) {//创建线程对象MyThread t1 = new MyThread();MyThread t2 = new MyThread();//给线程添加名字t1.setName("线程1");t2.setName("线程2");//启动两个线程t1.start();t2.start();}
}
  • 实现Runnable接口的方式进行实现

多线程的第二种启动方式:

  1. 自己定义一个类实现Runnable接口
  2. 重写里面的run方法
  3. 创建自己的类的对象
  4. 创建一个Thread类的对象,并开启线程

执行代码:

package MyThreads;
//创建线程二
//利用runnable接口进行创建
/*
* 多线程的第二种启动方式:
* 1.自己定义一个类实现Runnable接口
* 2.重写里面的run方法
3.创建自己的类的对象
4.创建一个Thread类的对象,并开启线程
*
* */public class Dom2 {public static void main(String[] args) {//创建线程需要执行的任务MyThread2 mr = new MyThread2();//创建线程执行mr任务Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);//给线程添加名字t1.setName("线程一");t2.setName("线程二");//开启线程t1.start();t2.start();}
}

接口代码:

package MyThreads;
//创建线程二
//利用runnable接口进行创建
/*
* 多线程的第二种启动方式:
* 1.自己定义一个类实现Runnable接口
* 2.重写里面的run方法
3.创建自己的类的对象
4.创建一个Thread类的对象,并开启线程
*
* */public class Dom2 {public static void main(String[] args) {//创建线程需要执行的任务MyThread2 mr = new MyThread2();//创建线程执行mr任务Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);//给线程添加名字t1.setName("线程一");t2.setName("线程二");//开启线程t1.start();t2.start();}
}
  • 利用Callable接口和Future接口方式实现

多线程的第三种实现方式:
特点:可以获取到多线程运行的结果

  1. 创建一个类MyCallable实现Callable接口
  2. 重写call (是有返回值的,表示多线程运行的结果 )
  3. 创建MyCallable的对象 (表示多线程要执行的任务)
  4. 创建FutureTask的对象 (作用管理多线程运行的结果)
  5. 创建Thread类的对象,并启动 (表示线程)

对象:

package MyThreads;import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;//线程创建3
/*
* 多线程的第三种实现方式:
特点:可以获取到多线程运行的结果
1。创建一个类MyCallable实现Callable接口
* 2。重写ca11   (是有返回值的,表示多线程运行的结果 )
3。 创建MyCallable的对象 (表示多线程要执行的任务)
* 4。创建FutureTask的对象 (作用管理多线程运行的结果)
* 5。创建Thread类的对象,并启动 (表示线程)
* */
public class Dom3 {public static void main(String[] args) throws ExecutionException, InterruptedException {//创建MyCallable的对象 (表示多线程要执行的任务)MyCallable mr = new MyCallable();//创建FutureTask的对象 (作用管理多线程运行的结果)FutureTask<Integer> ft = new FutureTask<>(mr);//创建Thread类的对象,并启动 (表示线程)Thread t1 = new Thread(ft);//启动线程t1.start();//获取返回值Integer result = ft.get();System.out.println(result);}
}

接口:

package MyThreads;import java.util.concurrent.Callable;public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= 100; i++) {sum = sum + i;}return sum;}
}

总结:
按照不同的需求去完成我们的线程创建
在这里插入图片描述

1.4 多线程的成员方法

方法名称说明
String getName()返回此线程的名称
void setName(string name)设置线程的名字(构造方法也可以设置名字)
static Thread currentThread()获取当前线程的对象
static void sleep(long time)让线程休眠指定的时间,单位为毫秒
setPriority(int newPriority)设置线程的优先级
final int getPriority()获取线程的优先级
final void setDaemon(boolean on)设置为守护线程
public static void yield()出让线程/礼让线程
public static void join()插入线程/插队线程

细节:(前面四个成员方法)

  • 细节1:
    如果我们没有设置名字,JVM会自动给我们设置Thread-x的名字,x从0开始。
    如果我们要设置名字。可以通过set设置,也可以用继承的Thread的对象,重写Thread的构造方法,利用有参构造
  • 细节2;
    当启动JVM时,就会创建多个线程,我们以前所写的代码都在main线程里面进行的
    细节3:
    sleep:哪条线程执行到该代码就会在这条线程上停留

测试代码:

package MyThreadThod;
//成员方法演示
/*
* 1. 设置名字,返回名字
* 2. 获取当前的线程名字
* 3. 让线程睡眠(单位毫秒)
* */
public class Dom1 {public static void main(String[] args) {//创建线程对象MyThread t1 = new MyThread("lisi");MyThread t2 = new MyThread("zhangsan");//开启线程t1.start();t2.start();}
}

继承Thread代码:

package MyThreadThod;public class MyThread extends Thread{public MyThread() {}public MyThread(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 100; i++) {//睡眠try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(getName()+"@"+i);}}
}

成员方法优先级
范围: 1~10
优先级越高,就代表的执行他的概率就越高,但是不是代表优先级越高,就先执行该代码,执行的顺序是随机的,但是优先级越大,随机的概率就越大

默认的优先级: 5

package MyThreadThod;
//成员方法,优先级
public class Dom2 {public static void main(String[] args) {//创建多线程执行的任务MyRunnable mr = new MyRunnable();//创建Thread对象,执行任务Thread t1 = new Thread(mr);//设置优先级t1.setPriority(10);//启动t1.start();}
}

守护线程:

这是表示“备胎守护线”,当所守护的对象不存在的时候就没有存在的必要了,就会提前结束线程

package MyThreadThod;
//成员方法:守护线程
public class Dom3 {public static void main(String[] args) {//创建Thread对象MyThread t1 = new MyThread();MyThread2 t2 = new MyThread2();//将第二个设置为守护线程t2.setDaemon(true);t1.setName("女神");t2.setName("二货");t2.start();t1.start();}
}

出让线程:

让执行的线程相对平均
Thread.yieId();

插入线程:

让该线程执行完后再执行该代码

package MyThreadThod;import MyThreads.MyThread1;//插入代码
public class Dom4 {public static void main(String[] args) throws InterruptedException {Thread t1 = new MyThread1();t1.setName("洋芋");t1.start();t1.join();for (int i = 0; i < 10; i++) {System.out.println("main线程" + i);}}
}

1.5 线程的生命周期

在这里插入图片描述

二、线程安全

在这里插入图片描述
买票引发的安全问题:

  1. 相同的票出现了多次
  2. 出现了超出范围的票

解决方法:
在这里插入图片描述

  • 利用锁
    特点1: 锁默认打开,有一个线程进去了,锁自动关闭
    特点2:里面的代码全部执行完毕,线程出来,锁自动打开

代码实现

package MySafeThread.safe1;public class MySafe  extends Thread{static int ticket = 0;//锁对象必须是唯一的static Object obj = new Object();@Overridepublic void run() {//创建锁//作用:如果有一个进入到了线程当中就会,就不会让其他的线程进入,直到//当该线程完成过后,重写再抢夺CPUwhile (true){   //创建锁synchronized (obj){if (ticket<100){//每个10毫秒在进行买票try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket++;System.out.println(getName() + "正在卖"+ticket+"张票了!!!!!!!!!");}else {break;}}}}
}

锁对象必须得是唯一的

锁不唯一就表示打开这扇门的钥匙有多个,不能让该代码执行完后,才能让其他的线程抢夺,这样会导致多执行,重复的效果

一般情况下用当前代码的字节码文件.class来表示唯一对象

1.6 同步方法

就是把synchronized关键字加到方法上

格式:
修饰符 synchronized 返回值类型 方法名(方法参数) {...}

特点1: 同步方法是锁住方法里面所有的代码
特点2:锁对象不能自己指定

里面在不同状态下调用的对象也不一样:

非静态: this
静态:当前类的字节码文件对象

Stirngbuilder 与 Stringbuffer 的区别:

  • Stringbuffer在多线程下相对于Stirngbuilder较为安全
  • 所以在单线程的情况下一般采用 Stringbuilder

主要修改的代码

package MySafeThread.safe2;import java.util.Timer;public class MyRunnable2 implements Runnable{int ticket = 0;@Overridepublic void run() {//1. 循环//2. 同步方法//3.判断是否完成,未完成,执行下列代码//4.判断是否完成,已完成,执行下列代码while (true){if (method()) break;}}//创建同步方法:这里是非静态的调用时为:thisprivate synchronized boolean method() {if (ticket == 100) {return true;} else {try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket++;System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "票了!!!!!");}return false;}
}

1.7 锁lock

定义:

虽然我们可以理解同步代码块和同步方法的锁对象问题但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

Lock中提供了获得锁和释放锁的方法

  • void lock(): 获得锁
  • void unlock(): 释放锁

手动上锁、手动释放锁

Lock是接口不能直接实例化

这里采用它的实现染ReentrantLock来实例化

ReentrantLock的构造方法
ReentrantLock():创建一个ReentrantLock的实例

1.8 死锁

两个锁互相嵌套导致,两个锁都在等待着对方放下锁,但是他们释放锁的情况是执行该所下面的代码才能释放,这样就导致程序卡死出不去

死锁实列:

在这里插入代码片

所以我们要在写代码的时候,千万不要进行锁的嵌套写法,一不小心就会掉进坑里面

package MySafeThread.BugLock;public class BUGLock extends Thread {static Object obj1 = new Object();static Object obj2 = new Object();@Overridepublic void run() {if ("A".equals(getName())){synchronized (obj1){System.out.println("拿到了线程A,准备拿线程B");synchronized (obj2){System.out.println("拿到了线程B,结束");}}} else if ("B".equals(getName())) {if ("B".equals(getName())){synchronized (obj2){System.out.println("拿到了线程B,现在拿线程A");synchronized (obj1){System.out.println("拿到了线程A,现在拿线程B");}}}}}
}

1.8 生产者和消费者 (等待唤醒机制)

生产者消费者模式是一个十分经典的多线程协作的模式

形象化理解:

在这里插入图片描述

方法名称说明
void wait()当前线程等待,直到被其他线程唤醒
void notify()随机唤醒单个线程
void notifyAll()唤醒所有线程

启动窗口:

package MyLock.WaitAndNotify;import java.io.FileOutputStream;public class waitNotiyDom {public static void main(String[] args) {//创建厨师和吃货的对象Cook cook = new Cook();FoodAddict addict = new FoodAddict();//设置名字cook.setName("厨师");addict.setName("吃货");cook.start();addict.start();}
}

厨师窗口:

package MyLock.WaitAndNotify;public class Cook extends Thread{@Overridepublic void run() {//循环while (true){//创建同步代码块synchronized (Desk.lock){//判断,没有执行到尾部if (Desk.MaxFood == 0){break;}else {//执行到末尾//首先判断状态值是否为0,是否有面条if (Desk.Food == 1){//是1,就执行等待try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else {//是0,就做食物,释放所有等待代码System.out.println("厨师正在做食物");System.out.println("厨师已做完,请吃");//将状态码变为1Desk.Food =1;//释放所以等待代码Desk.lock.notifyAll();}}}}}
}

吃货窗口:

package MyLock.WaitAndNotify;public class FoodAddict extends Thread{@Overridepublic void run() {while (true){synchronized (Desk.lock){//判断是否还能吃下去if (Desk.MaxFood == 0){//不能吃下去了System.out.println("吃货吃不下了,要g了");break;}else{//还能继续吃//判断桌子上面是否有吃的if (Desk.Food == 0){//没有吃的就执行等待try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else {//有吃的//将吃的碗数减1Desk.MaxFood--;System.out.println("吃货正在吃面条,还可以吃"+Desk.MaxFood+"碗");//已经吃完,释放所有的代码,叫厨师吃饭了Desk.lock.notifyAll();//食物清空Desk.Food =0;}}}}}
}

桌子窗口:

package MyLock.WaitAndNotify;
//表示桌子,来放置面条的状态,达到谁去执行的效果
public class Desk{//创建面的状态 , 0:没有面条 1:有面条public static int Food = 0;//创建吃货吃的最多碗public static int MaxFood = 10;//创建锁public static Object lock = new Object();}

1.9 等待唤醒机制(阻塞队列方式实现)

在这里插入图片描述
实现类:
ArrayBlockingQueue底层是数组,有界限
LinkedBlockingQueue底层是链表,无界但不是真正的无界最大为int的最大值。

厨师类

package MyLock.ArrayBlockingQueues;import java.util.concurrent.ArrayBlockingQueue;public class queueCooker extends Thread{ArrayBlockingQueue<String> queue;public queueCooker(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true){//不需要再写锁了,因为put当中写了try {queue.put("面条");System.out.println("厨师做了面");} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

吃货类

package MyLock.ArrayBlockingQueues;import java.util.concurrent.ArrayBlockingQueue;public class queueAddict extends Thread{ArrayBlockingQueue<String> queue;public queueAddict(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true){try {String food = queue.take();System.out.println("吃货在吃"+food);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

执行类:

package MyLock.ArrayBlockingQueues;import java.util.Arrays;
import java.util.concurrent.ArrayBlockingQueue;//阻塞队列方式实现等待唤醒任务
public class ArrayBlockingQueueDom {public static void main(String[] args) {//创建阻塞队列方式(唯一)ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);//创建两个变量,赋名字,并且把阻塞队列传入queueAddict addict = new queueAddict(queue);queueCooker cooker = new queueCooker(queue);addict.setName("吃货");cooker.setName("厨师");//开启线程addict.start();cooker.start();}
}

1.10 线程状态

六种红色的状态
在这里插入图片描述
新建状态(NEW ) --> 创建线程对象

就绪状态(RUNNABLE ) --> start方法

阻塞状态( BLOCKED ) --> 无法获得锁对象

等待状态 (WAITING ) --> wait方法

计时等待 (TIMED WAITING ) --> sleep方法

结束状态(TERMINATED ) --> 全部代码运行完毕

二、线程池

2.1 线程池的概述

  • 以前写多线程的弊端
  1. 用到线程的时候就创建
  2. 用完之后线程消失

会造成资源的浪费

所以现在出现了线程池来存储线程

线程池的核心原理:

  1. 创建一个池子,池子中是空的
  2. 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
  3. 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待

创建线程池的方法:
Executors:线程池的工具类通过调用方法返回不同类型的线程池对象

方法名称说明
public static ExecutorService newCachedThreadPool()创建一个没有上限的线程池
public static ExecutorService newFixedThreadPool(int nThreads)创建有上限的线程池
package MythreadPool.dom1;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;//创建线程池,初识线程池
public class Dom1 {public static void main(String[] args) {//创建无上限的线程池ExecutorService pool1 = Executors.newCachedThreadPool();//创建有上限的线程池(3个)
//        ExecutorService pool2 = Executors.newFixedThreadPool(3);//提交任务pool1.submit(new MyRunnable1());pool1.submit(new MyRunnable1());pool1.submit(new MyRunnable1());pool1.submit(new MyRunnable1());}
}package MythreadPool.dom1;public class MyRunnable1 implements Runnable{@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(Thread.currentThread().getName() + "-----------" + i);}}
}

2.2 自定义线程池

任务拒绝策略说明
ThreadPoolExecutor.AbortPolicy 默认策略: 丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy丢弃任务,但是不抛出异常 这是不推荐的做法
ThreadPoolExecutor.DiscardoldestPolicy抛弃队列中等待最久的任务 然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunsPolicy调用任务的run()方法绕过线程池直接执行

理解:
自定义线程池:

核心元素一: 正式员工数量 --------------> 核心线程数量(不能小于0)

核心元素二: 餐厅最大员工数 ---------------> 线程池中最大线程的数量(最大数量 >= 核心线程数量)

核心元素三: 临时员工空闲多长时间被辞退(值) --------> 空闲时间 (值) (不能小于0)

核心元素四: 临时员工空闲多长时间被辞退(单位) -------> 空闲时间(单位) (用TimeUnit指定)

核心元素五: 排队的客户 --------> 阻塞队列 (不能为null)

核心元素六: 从哪里招人 ---------> 创建线程的方式(不能为null)

核心元素七: 当排队人数过多,超出顾客请下次再来(拒绝服务) ------> 要执行的任务过多时的解决方案(不能为null)

package MythreadPool.dom2;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;//自定义线程池
public class dom2 {public static void main(String[] args) {ThreadPoolExecutor pool = new ThreadPoolExecutor(3 , //核心线程数量(表示的正式员工)6, //最大线程数量(总员工数量)60,//空闲线程最大的存活时间TimeUnit.SECONDS, //表示当前最大存活的时间单位new ArrayBlockingQueue<>(3),//排队的客户Executors.defaultThreadFactory(),//创建线程工厂new ThreadPoolExecutor.AbortPolicy()//拒绝方法);}
}

总结:

  • 创建一个空的池子
  • 有任务提交时,线程池会创建线程去执行任务,执行完毕归还线程

不断的提交任务,会有以下三个临界点:

  1. 当核心线程满时,再提交任务就会排队
  2. 当核心线程满,队伍满时,会创建临时线程
  3. 当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略

2.3 线程池到底多大才合适

CPU 密集型运算 --------> 最大并行数 +I

l/0 密集型运算 ----------> 最大并行数 * ”期望 CPU利用率 * 总时间(CPU计算时间+等待时间) / CPU计算时间

例如: 4核8线程

在这里插入图片描述

相关文章:

Java基础学习(16)多线程

Java基础学习多线程 一、多线程1.1 什么是多线程1.2 多线程的两个概念1.2.1 并发 1.3 多线程的实现方式1.4 多线程的成员方法1.5 线程的生命周期 二、线程安全1.6 同步方法1.7 锁lock1.8 死锁1.8 生产者和消费者 (等待唤醒机制)1.9 等待唤醒机制(阻塞队列方式实现&#xff09;1…...

【一起啃书】《机器学习》第五章 神经网络

文章目录 第五章 神经网络5.1 神经元模型5.2 感知机与多层网络5.3 误差逆传播算法5.4 全局最小与局部极小5.5 其他常见神经网络5.6 深度学习 第五章 神经网络 5.1 神经元模型 神经网络是由具有适应性简单单元组成的广泛并行互连的网络&#xff0c;它的组织能够模拟生物神经系统…...

matlab实验二可视化

学聪明点&#xff0c;自己改&#xff0c;别把我卖了 一、实验目的及要求 要求 1、掌握 MATLAB常用的二维和三维绘图函数 2、掌握MATLAB的图形注释 3、熟悉MATLAB常用的图形修饰 4、熟悉MATLAB的图形动画 实验原理 1、MATLAB二维绘图&#xff1a;plot,fplot,fimplicit&#xf…...

(数据结构)栈的实现——再一次保姆级教学

目录 1. 栈 ​编辑 1.2 栈的实现 2. 代码的实现 2.1 初始化栈和销毁栈 2.2栈顶元素的插入 2.3栈顶元素的删除 栈元素删除 2.4栈顶元素的获取和栈元素的个数 1. 栈 1.1 栈的概念和结构 栈(Stack)是一种线性存储结构&#xff0c;它具有如下特点&#xff1a; &#xff0…...

【5G RRC】RSRP、RSRQ以及SINR含义、计算过程详细介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…...

K8s(Kubernetes)学习(一):k8s概念及组件

Kubernetes中文文档&#xff1a;https://kubernetes.io/zh-cn/docs/home/ Kubernetes源码地址&#xff1a;https://github.com/kubernetes/kubernetes 一:Kubernetes是什么 首先要了解应用程序部署经历了以下几个时代&#xff1a; 传统部署时代&#xff1a;在物理服务器上运…...

Web3 常用语和黑话你知道吗?

My friend Dave used to be a bagholder, but he FOMO’d and bought even more BTC. Now, he’s a big whale HODLing for that moon. …that’s a lot to take in for just two sentences. If you’re new to Bitcoin and the world of cryptocurrencies, we understand if …...

物联网和边缘计算:如何将数据处理和决策推向设备边缘

第一章&#xff1a;引言 当我们谈论物联网&#xff08;IoT&#xff09;时&#xff0c;我们通常指的是将各种设备连接到互联网&#xff0c;并通过数据交换来实现智能化的网络。然而&#xff0c;传统的物联网模型通常涉及将数据发送到云端进行处理和分析。然而&#xff0c;随着技…...

【Android学习专题】java基本语法和概念(学习记录)

学习记录来自菜鸟教程 Java 变量 Java 中主要有如下几种类型的变量 局部变量 在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中&#xff0c;方法结束后&#xff0c;变量就会自动销毁类变量&#xff08;静态变量&#xff09; 类变量也声…...

Android系统启动全流程分析

当我们买了一个手机或者平板&#xff0c;按下电源键的那一刻&#xff0c;到进入Launcher&#xff0c;选择我们想要使用的某个App进入&#xff0c;这个过程中&#xff0c;系统到底在做了什么事&#xff0c;伙伴们有仔细的研究过吗&#xff1f;可能对于Framework这块晦涩难懂的专…...

RabbitMQ --- 惰性队列、MQ集群

一、惰性队列 1.1、消息堆积问题 当生产者发送消息的速度超过了消费者处理消息的速度&#xff0c;就会导致队列中的消息堆积&#xff0c;直到队列存储消息达到上限。之后发送的消息就会成为死信&#xff0c;可能会被丢弃&#xff0c;这就是消息堆积问题。 解决消息堆积有三种…...

1.Buffer_Overflow-1.Basic_Jump

github上面的练习题 git clone https://github.com/Adamkadaban/LearnPwn 然后开始做 先进行 readelf 然后进行执行看看 是怎么回事 ./buf1发现就是一个输入和输出 我们checksec看看 发现stack 保护关闭 开启了NX保护 我们进入ida64看看反汇编 我习惯先看看字符串 SHITF…...

MySQL入门语法第三课:表结构的创建

数据表结构 定点数类型decimal(m,d) m表示数字总位数 d表示小数位数 ★创建数据表先要选择数据库 1 . CREATE TABLE 表名称 创建数据表 (字段名1 数据类型1 [,字段名2 数据名2] [, .....] ); 一个字段写一行 修改表名 alter table 旧表名 rename 新表名…...

SpringSecurity框架学习与使用

SpringSecurity框架学习与使用 SpringSecurity学习SpringSecurity入门SpringSecurity深入认证授权自定义授权失败页面权限注解SecuredPreAuthorizePostAuthorizePostFilterPreFilter 参考 SpringSecurity学习 SpringSecurity入门 引入相关的依赖&#xff0c;SpringBoot的版本…...

DHCP+链路聚合+NAT+ACL小型实验

实验要求: 1.按照拓扑图上标识规划网络。 2.使用0SPF协议进程100实现ISP互通。 3.私网内PC属于VLAN1O, FTP Server属于VLAN2O,网关分 别为所连接的接入交换机&#xff0c;其中PC要求通过DHCP动态获取 4:私网内部所有交换机都为三层交换机&#xff0c;请合理规划VLAN&#…...

西瓜书读书笔记整理(三)—— 第二章 模型评估与选择

第二章 模型评估与选择 第 2 章 模型评估与选择2.1 经验误差与过拟合1. 错误率 / 精度 / 误差2. 训练误差 / 经验误差 / 泛化误差3. 过拟合 / 欠拟合4. 学习能力5. 模型选择 2.2 评估方法1. 评估方法概述2. 留出法3. 交叉验证法4. 自助法5. 调参 / 最终模型 2.3 性能度量1. 回归…...

AcWing算法提高课-1.3.6货币系统

宣传一下算法提高课整理 <— CSDN个人主页&#xff1a;更好的阅读体验 <— 本题链接&#xff08;AcWing&#xff09; 点这里 题目描述 给你一个n种面值的货币系统&#xff0c;求组成面值为m的货币有多少种方案。 输入格式 第一行&#xff0c;包含两个整数n和m。 接…...

vue3回到上一个路由页面

学习链接 Vue Router获取当前页面由哪个路由跳转 在Vue3的setup中如何使用this beforeRouteEnter 在这个路由方法中不能访问到组件实例this&#xff0c;但是可以使用next里面的vm访问到组件实例&#xff0c;并通过vm.$data获取组件实例上的data数据getCurrentInstance 是vue3提…...

Linux三种网络模式 | 仅主机、桥接、NAT

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Linux三种网络模式 仅主机模式&#xff1a;虚拟机只能访问物理机&#xff0c;不能上网 桥接模式&#xff1a;虚拟机和物理机连接同一网络&#xff0c;虚拟机和物理机…...

数据库设计与前端框架

数据库设计与前端框架 学习目标&#xff1a; 理解多租户的数据库设计方案 熟练使用PowerDesigner构建数据库模型理解前端工程的基本架构和执行流程 完成前端工程企业模块开发 多租户SaaS平台的数据库方案 多租户是什么 多租户技术&#xff08;Multi-TenancyTechnology&a…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...