多线程(基础)

  前言👀~

上一章我们介绍了什么是进程,对于进程就了解那么多即可,我们作为java程序员更关注线程,线程内容比较多,所以我们要分好几部分才能讲完

目录

进程的缺点

 多线程(重要)

进程和线程的区别(经典面试题)

java如何进行多线程编程?

创建线程的方式(面试题)

继承Thread类(来自java.lang包下)

使用匿名内部类继承Thread类重写run

实现Runnable接口

使用匿名内部类实现Runnable接口重写run

使用lambda表达式创建线程(推荐)

Thread 类及常见方法

Thread类的构造方法

Thread类的常见属性

启动线程

中断线程

等待线程

查看线程状态


如果各位对文章的内容感兴趣的话,请点点小赞,关注一手不迷路,如果内容有什么问题的话,欢迎各位评论纠正 🤞🤞🤞

12b46cd836b7495695ce3560ea45749c.jpeg

个人主页:N_0050-CSDN博客

相关专栏:java SE_N_0050的博客-CSDN博客  java数据结构_N_0050的博客-CSDN博客  java EE_N_0050的博客-CSDN博客


进程的缺点

多进程编程的缺点:进程太重量效率不高,创建进程和销毁进程和调度进程消耗的时间都是比较多的(消耗在申请资源上),因为我们知道进程是系统资源分配的基本单元,所以在给进程分配资源的时候是一个大活。拿分配内存说,操作系统内部也有一定的数据结构,用来管理空闲的内存,当进程申请内存空间的时候,操作系统就会从这个数据结构中找到大小合适空闲的内存返回给进程。这里的数据结构可以提高一定的效率,但是总体来说和线程相比还是比较耗时的。同时频繁创建和销毁进程和进程切换是一个开销很大的操作,多进程和多线程都能实现并发编程,线程比进程更轻量。有些任务场景需要 “等待 IO”, 为了让等待 IO 的时间能够去做⼀些其他的工作, 也需要用到并发编程. 其次,虽然多进程也能实现 并发编程, 但是线程比进程更轻量


 多线程(重要)

进程想要执行任务就需要依赖线程线程不能独立存在,需要依附于进程(进程包含线程,进程可包含一个线程也可包含多个线程),一个进程在最开始的时候,至少要有一个线程(主线程),这个线程负责完成执行代码的工作,我们也可以根据需要创建多个线程,从而实现"并发编程"的效果换句话说,就是进程中的最小执行单位就是线程

线程也称轻量级线程(创建、销毁、调度都比进程快),每个线程就是一个独立的 "执行流"(因为在执行用户写的代码)可以独立的执行一些代码,每一个线程可以执行一系列的操作(也就是代码)

更好的理解线程,还是拿之前在进程举的演员的例子,一个舞台可以有多个演员,但是呢这个多个演员来自不同的剧组,这里面的演员就是线程,剧组就是进程。在我们之前谈的进程调度都是基于一个进程只有一个线程,可以理解为之前每个剧组都只有一个演员。实际上,一个进程可以有多个线程,每个线程都可以独立进行调度。之后谈到进程调度的话,不是调度整个进程,而是调度进程中的每一个线程。就比如说这个导演叫这个剧组的所有人来拍戏,所有的演员由导演进行分配角色、上场时间、台词等,相当于线程也有状态、优先级、上下文、记账信息

下面有图更好理解多线程
 
一个工厂代表一个进程,一个生产线代表一个线程,我们之前提到的多进程是下面这样子的 


下面这样是我们说的多线程,一个进程中有多个线程,同个工厂(共用资源)两个生产线(多线程)提高了效率


下面这么多人吃100个坤,适当的线程数目(里面的人)能提高效率,但是线程数目过多的情况,效率会降低并且造成线程冲突(线程不安全)


当人数过多且有人吃不到坤的时候,生气了把鸡全扔了,此时引发线程异常,如果我们没有处理好,可能会导致整个进程崩了,其他线程也会随之消失


总结:一个进程使用PCB来表示,一个进程可以使用一个PCB表示也可与使用多个PCB表示,每个TCB对应一个线程(可以理解PCB中包含TCB),并且每个线程都有这些信息(状态、优先级、上下文、记账信息,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈)辅助调度,除此之外,前面说到的属性pid是相同的,内存指针、文件描述符表也是共用一份的,根据这些信息我们可以得出线程的特点:每个线程都可以独立去cpu上调度执行、同一个线程的多个进程之间共用一份内存空间和文件资源,所以我们创建线程的时候不需要像进程一样申请资源(但是呢创建第一个线程的时候,相当于和进程一起创建的,所以我们要去申请资源,这里申请资源算在进程头上。后续再创建线程的话就是共享同一份了),我们直接用系统给进程分配好的资源,这样大大提高了我们的效率和节省开销。综上所述我们可以得出进程是资源分配的基本单位,线程是CPU 调度执行的基本单位

每个线程都是独立调度的,在调度的过程中,系统就不考虑 进程 这样的概念了。所以就是不同进程中的线程可以被轮番调度,每个线程只有获得 CPU 的使用权才能执行指令。所谓多线程的并发运行,其实是指从宏观上看,各个线程轮流获得 CPU 的使用权,分别执行各自的任务。


进程和线程的区别(经典面试题)

1.线程比进程更轻量、高效,线程不需要申请资源,和同一进程共用一份,省去了申请资源的开销

2.同一个进程内线程和线程之间会有影响(线程不安全和线程异常),一个线程崩了可能导致其他线程受到影响最终导致进程崩了。进程和进程之间具有独立性

3.线程依附于进程,一个进程至少有一个线程(主线程),也可以有多个线程

4.线程是调度执行的基本单位,进程是资源分配的基本单位


java如何进行多线程编程?

线程是操作系统的概念,操作系统提供了一套API来操作线程,java对操作系统提供的API进行封装(跨平台),我们学java的只需要掌握java封装过后的这套API就可以操作线程了

进程和进程之间能并发执行实现并发编程,线程和线程之间也能实现并发执行实现并发编程,我们学java的更侧重线程之间的并发执行

创建线程的方式(面试题)

继承Thread类(来自java.lang包下)

创建Thread对象,我们就可以操作 操作系统内部的线程了。以及重写入口方法run(描述了该线程要执行的任务)

class MyThread extends Thread {@Overridepublic void run() {System.out.println("我开始工作了");System.out.println("我结束工作了");}
}public class test1 {public static void main(String[] args) {Thread thread = new MyThread();thread.start();System.out.println("我是主线程");}
}

输出:不确定

再来看下面这段代码,猜一下输出顺序

class MyThread extends Thread {@Overridepublic void run() {while (true) {System.out.println("thread线程正在工作");}}
}public class test1 {public static void main(String[] args) {Thread thread = new MyThread();thread.start();while (true) {System.out.println("主线程正在工作");}}
}

输出:交替输出


为什么呢?我们也不知道这两个线程是同时执行的还是交替执行的(同一个核心上执行,还是分别在两个核心上执行),所以我们统称并发(并行+并发),实现并发编程的效果,为什么要实现并发编程?(充分利用多核cpu的资源)。

注意:打印顺序不一定,操作系系统对于多个线程的调度顺序是不确定的,随机的。虽然有先后顺序但是谁先谁后我们是不确定的(重要),这个随机取决于操作系统对于线程调度的模块(调度器)的具体实现

接着再来看下面这段代码,猜一下输出顺序

class MyThread extends Thread {@Overridepublic void run() {while (true) {System.out.println("thread线程正在工作");}}
}public class test1 {public static void main(String[] args) {Thread thread = new MyThread();thread.run();while (true) {System.out.println("主线程正在工作");}}
}

输出:thread线程正在工作   不只一条哈

原因:T.run 这种时候只有一个主线程,因为我们没有创建一个新的进程。等run这个方法结束后,才会执行后面的代码,相当于只有一个执行流,只能依次执行循环。相当于就是main线程在执行它的run方法,就跟我们在main方法中平常创建一个类然后调用方法一样,main线程在工作

不信的话代码拿去自己试试,然后用jdk中自带的工具jconsole,里面其他线程是JVM创建的

使用匿名内部类继承Thread类重写run

同样创建Thread对象,我们就可以操作 操作系统内部的线程了。以及重写入口方法run(描述了该线程要执行的任务)

public class test1 {public static void main(String[] args) {Thread thread = new Thread() {@Overridepublic void run() {System.out.println("我是匿名内部类");}};thread.start();System.out.println("我是主线程");}
}

实现Runnable接口

实现Runnable接口,它就表示的是一个可以运行的任务,所以还是需要创建线程来完成这个任务。这样理解我写了个任务,丢给线程去完成,但是我们要先把线程创建出来才能去完成

class MyRunnable implements Runnable {@Overridepublic void run() {while (true) {System.out.println("我是Runnable接口");}}
}public class test1 {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable);thread.start();while (true) {System.out.println("主线程正在工作");}}
}

使用Runnable接口和继承Thread类的区别:主要是为了解耦合,使用Runnable接口相当于跟线程拆分开,把任务抽离出来,可以把这个任务丢给任意一个线程去完成,可以用在需要重复完成这个任务的场景,直接继承Thread类就不行,它更适合完成一次性的任务

还有关于创建一个线程的时候,有两个关键的操作。一个是明确线程要执行的任务,我们更关注任务本身,如果这个任务就只是执行一段简单的代码至于用什么方式实现这个任务没什么区别。如果遇到复杂的任务有些方式可能就完成不了,这时候我们需要用其他的方式去完成,这时候我们把任务提取出来,我们可以自己选择指定的方式去完成这个任务    。另外一个操作就是通过调用系统API创建出线程。

使用匿名内部类实现Runnable接口重写run

public class test1 {public static void main(String[] args) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("我是匿名内部类");}}) {};thread.start();System.out.println("我是主线程");}
}

使用lambda表达式创建线程(推荐)

public class test1 {public static void main(String[] args) {Thread thread = new Thread(() -> {System.out.println("我是lambda表达式");});thread.start();System.out.println("我是主线程");}
}

除了上面的方式之外,还有其他的方式,后续讲解,还有一个点需要清楚就是创建Thread类的时候并没有真正创建线程,只有在调用start方法的时候调用系统API去创建线程的时候才算认识


Thread 类及常见方法

Thread类的构造方法

在Thread类源码里有个构造方法可以设置线程的名字,其他的就没什么好介绍的了

public class test1 {public static void main(String[] args) {Thread thread = new Thread(() -> {while (true) {System.out.println("我是lambda表达式");}}, "我叫线程A");thread.start();}
}

为什么这里没有显示main线程呢?因为main线程执行完了,线程的入口方法执行完了,这个线程自然就销毁了。对于主线程来说,入口方法就是main方法,它调用系统API去创建完线程之后就执行完了。所以如果线程都执行完了,进程也就结束了但只要有一个线程还在执行,进程就不会结束
 


Thread类的常见属性


1.ID:线程的身份标识就是用来区分线程的,类似进程的pid,只不过这里的ID是java提供的,不是系统api提供的

2.getState()方法:获取线程状态,后面有讲到

3.isDaemon()方法判断是否为后台线程(守护线程),守护线程就是用来告诉JVM,我的这个线程不重要不需要等待它运行完才退出,让JVM喜欢什么时候退出就退出。前台线程(非守护线程)就是告诉JVM,这个线程没执行完成之前,你不能退出。默认情况一个线程是前台线程守护进程(后台进程),默认情况一个线程是前台线程我们可以通过setDameon()方法去设置,这么设置之后主线程执行完后,没有其他前台线程了,这个进程自然也就结束了

public class test1 {public static void main(String[] args) {Thread thread = new Thread(() -> {while (true) {System.out.println("我是lambda表达式");}}, "我叫线程A");thread.setDaemon(true);//设置thread线程为守护线程thread.start();}
}

4.isAlive()方法:Thread对象的生命周期要比系统内核中的线程更长,线程没了Thread对象还在,我们要以系统内核中的线程为主。所以我们使用isAlive()进行判断,判断系统内核中的线程有没有结束,结束返回false,没结束返回true。简单的理解为 run 方法是否运行结束了

public class test1 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("我是lambda表达式");}, "我叫线程A");System.out.println(thread.isAlive());thread.start();System.out.println(thread.isAlive());Thread.sleep(2000);System.out.println(thread.isAlive());}
}

输出:false true 我是lambda表达式 false

5.isInterrupted()方法:用来判断线程是否被中断,怎么判断呢?Thread内部有一个标志位进行判断。下面会讲到


启动线程

使用start()方法创建线程

start方法和run方法的区别:非常直白的说,你可以把run看作是任务,start是叫人过来完成这个任务的。专业点说就是start方法通过调用系统API在系统内核中创建线程然后执行run方法中的代码。run方法会在线程创建好后自动被被调用


中断线程

中断一个线程(终止/打断),让一个线程停止运行(销毁),在java中销毁/终止一个线程做法比较唯一(这个唯一不是说只有一个方法,而是说销毁一个进程是让run方法快点执行完),让run方法快点执行完。在C++中是可以直接强制终止一个线程的运行,就比如你打开一个文本编辑器输入一段信息,输入一半直接给你干没了

先补充两个方法currentThread()方法和sleep()方法后面要用到

获取当前线程引用:currentThread()方法返回当前线程对象的引用,哪个线程调用这个方法就获取哪个线程对象的引用

休眠当前线程:sleep()方法让线程睡觉的,你可以设置睡多久,然后时间到了系统把它叫醒(阻塞->就绪),注意睡醒之后不会马上回到cpu上运行,要排队,这里会涉及调度所以会有一定的开销。就是你睡醒了需要一点时间缓缓才能去工作。

interrupt()方法:使用interrupt()方法设置标志位,前面说了Thread内部有一个标志位用来判断线程是否结束,调用这个方法就把这个标志位设置成true。这样即使我们还在执行sleep方法,它也会被强制唤醒。

来猜猜下面执行代码线程会是什么状态以及输出什么

public class test1 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("hello");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});thread.start();Thread.sleep(3000);thread.interrupt();}
}

答案是输出几个hello后报异常,然后说说过程,这个线程先执行sleep方法,然后这个线程处于睡眠,然后你使用了interrupt()方法设置把标志位为true,把这个线程唤醒了,那么这个线程就会继续工作,除非你不让它睡眠并且设置标志位为true。举个例子本来你在上班,然后突然有点困了,你同事让你睡会,结果领导来了你同事赶紧把你叫醒,你立马起来了。

使用interrupt()方法搭配sleep方法这样设置的标志位就像没效果一样,没有把你的线程中断,为什么这样设定呢?java期望线程收到中断的信息的时候,我们能够自己决定接下来要怎么处理,这样可以让我们在开发中有更多的操作空间,前提是通过异常的方式去唤醒。比如你打游戏女朋友叫你陪她去逛街,你可以直接关掉游戏陪她去,也可以说等我打完这把,还可以当个聋子啥也没听见。

public class test1 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("hello");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();
//            第一种   System.out.println("一直工作");
//                    
//            第二种   System.out.println("工作一会休息了");
//                    break;
//                    
//            第三种   break;}}});thread.start();Thread.sleep(3000);thread.interrupt();}
}

等待线程

使用join()方法,让一个线程等待另外一个线程执行结束再执行。前面说线程并发执行的时候,执行的顺序是不确定的随机的,此时我们可以通过这个方法来控制线程结束的顺序。并且我们可以设置等待时间,如果没有设置等待时间,默认的话这个线程会一直等到那个线程执行结束后才会执行自己的任务,类似舔狗有一直舔的也有舔一段时间不舔了

join方法的工作过程:

1.如果主线程正在运行,主线程中调用了A.join方法,此时主线程进入阻塞状态,A线程执行完,主线程才会解除完成接下来的任务

public class test1 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {for (int i = 0; i < 5; i++) {System.out.println("我先干完活,你再干");}});thread.start();System.out.println("我要干活了");thread.join();System.out.println("轮到我干活了");}
}

输出结果

2.如果主线程任务已经执行完了,再调用A.join方法,就不用进入阻塞状态了,直接结束了

public class test1 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {for (int i = 0; i < 5; i++) {System.out.println("我先干完活,你再干");}});thread.start();System.out.println("我马上干完不给你机会干");thread.join();}
}

输出结果


查看线程状态

和进程一样,线程也有运行、就绪、阻塞状态。真正在系统调度的还是线程,线程是调度执行的基本单位

在java中,给线程赋予了一些其他的状态:

1.NEW:Thread对象已经存在了,但是系统线程还没创建,也就是还没调用start方法

2.RUNNABLE:就绪状态,这里有两种表示,一种是在cpu上执行了,另一种是正等着去cpu上执行

3.TIMED_WATING:阻塞,被sleep这种固定时间的方式打断产生的阻塞

4.WATING:阻塞,被wait这种不固定时间的方式打断产生的阻塞

5.BLOCKED:阻塞,等待锁导致的阻塞,后续死锁讲解

6.TERMINATED:Thread对象还在,操作系统内核的线程结束了,也这样理解意味着该线程已经完成了其run方法的执行

public class test1 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("java");try {Thread.sleep(2000);} catch (InterruptedException e) {break;}}});System.out.println(t.getState());//只创建了Thread对象的NEW状态t.start();System.out.println(t.getState());//创建完线程t就是这个RUNNABLE状态Thread.sleep(1000);System.out.println(t.getState());//t线程处于睡眠t.interrupt();Thread.sleep(1000);System.out.println(t.getState());//t线程执行完了}
}

输出结果

后续线程出现卡死的情况,我们可以通过上述后面三种状态去确定卡死的原因是什么,最后两个状态后续再讲解

上述方法是多线程中非常常用的方法,要好好掌握,今天的内容就先到这,后面我们接着讲解线程还有不少的知识点等着💕

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

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

相关文章

数据结构速成--树和二叉树

由于是速成专题&#xff0c;因此内容不会十分全面&#xff0c;只会涵盖考试重点&#xff0c;各学校课程要求不同 &#xff0c;大家可以按照考纲复习&#xff0c;不全面的内容&#xff0c;可以看一下小编主页数据结构初阶的内容&#xff0c;找到对应专题详细学习一下。 气死了…

昇思25天学习打卡营第4天|数据集Dataset

数据集 Dataset 介绍 之前说过&#xff0c;MindSpore是基于Pipeline&#xff0c;通过Dataset和Transformer进行数据处理。Dataset在其中是用来加载原始数据的。mindSpore提供了数据集加载接口&#xff0c;可以加载文本、图像、音频等&#xff0c;同时也可以自定义加载接口。此…

乾坤微服务的使用

前言&#xff1a; 在这里整理下用乾坤来开发微服务的一些资料。 使用好处&#xff1a; 使用乾坤可以实现什么效果呢&#xff1f;众所周知&#xff0c;前端的框架五花八门&#xff0c;react/vue/angular等各领风骚&#xff0c;那么如果我们有需要把不同技术栈的项目整合起来&…

Vue3学习笔记<->创建第一个vue项目

新建一个项目目录 找一个盘新建一个目录&#xff0c;我这里在D盘创建一个vuedemo目录作为项目存放的目录。使用idea打开目录。   单击ieda底部的按钮“Terminal”&#xff0c;打开命令行窗口&#xff0c;如果命令行窗口当前目录不是“vuedemo”&#xff0c;就切换到“vuedem…

文本分类-RNN-LSTM

1.前言 本节介绍RNN和LSTM&#xff0c;并采用它们在电影评论数据集上实现文本分类&#xff0c;会涉及以下几个知识点。 1. 词表构建&#xff1a;包括数据清洗&#xff0c;词频统计&#xff0c;词频截断&#xff0c;词表构建。 2. 预训练词向量应用&#xff1a;下载并加载Glove的…

Vue2 - 首页登录实现随机验证码组件的封装与实现详解(详细的注释及常见问题汇总)

在网站首页等登录时,随机验证码在现代网络应用中扮演着重要的安全角色。为了帮助开发者轻松集成和使用随机验证码功能,本文将介绍如何利用 Vue.js 2 封装一个简单而功能强大的随机验证码组件。让你能够快速理解并应用这一组件到你的项目中。 一、解决方案 本文提供了完美便捷…

上海计算机考研避雷,25考研慎报

上大计算机一直很热 408考研er重来没有让我失望过&#xff0c;现在上大的专业课是11408&#xff0c;按理说&#xff0c;这个专业课的难度是很高的&#xff0c;但是408er给卷出了新高度&#xff0c;大家可以去上大官网看看今年最新的数据&#xff0c;我也帮大家统计了24年最新的…

Redis集群(Clustering in Redis)工作机制详解

Redis集群工作机制详解 Redis 集群是用于提高 Redis 可扩展性和高可用性的解决方案。 维基百科&#xff1a;Scalability is the property of a system to handle a growing amount of work by adding resources to the system. 可扩展性是系统的一种允许通过增加系统资源来处…

《Windows API每日一练》6.4 程序测试

前面我们讨论了鼠标的一些基础知识&#xff0c;本节我们将通过一些实例来讲解鼠标消息的不同处理方式。 本节必须掌握的知识点&#xff1a; 第36练&#xff1a;鼠标击中测试1 第37练&#xff1a;鼠标击中测试2—增加键盘接口 第38练&#xff1a;鼠标击中测试3—子窗口 第39练&…

Linux Static calls机制

文章目录 前言一、简介二、Background: indirect calls, Spectre, and retpolines2.1 Indirect calls2.2 Spectre (v2)2.3 RetpolinesConsequences 2.4 Static callsHow it works 三、其他参考资料 前言 Linux内核5.10内核版本引入新特性&#xff1a;Static calls。 Static c…

计算机毕业设计hadoop+spark+hive知识图谱医生推荐系统 医生数据分析可视化大屏 医生爬虫 医疗可视化 医生大数据 机器学习 大数据毕业设计

测试过程及结果 本次对于医生推荐系统测试通过手动测试的方式共进行了两轮测试。 &#xff08;1&#xff09;第一轮测试中执行了个20个测试用例&#xff0c;通过16个&#xff0c;失败4个&#xff0c;其中属于严重缺陷的1个&#xff0c;属于一般缺陷的3个。 &#xff08;2&am…

Spark SQL 的总体工作流程

Spark SQL 是 Apache Spark 的一个模块,它提供了处理结构化和半结构化数据的能力。通过 Spark SQL,用户可以使用 SQL 语言或 DataFrame API 来执行数据查询和分析。这个模块允许开发者将 SQL 查询与 Spark 的数据处理能力结合起来,实现高效、优化的数据处理。下面是 Spark S…

Spring Boot中实现定时任务最常用的方法 @Scheduled 注解和 TaskScheduler 接口【包含详情代码】

Spring Boot中实现定时任务最常用的方法 Scheduled 注解和 TaskScheduler 接口【包含详情代码】 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把手教你开发炫酷的vbs脚本制作(完善中………

CogMG:用大模型解决知识图谱覆盖不足的问题

CogMG&#xff1a;用大模型解决知识图谱覆盖不足的问题 提出背景知识图谱的作用知识覆盖不完整知识更新不对齐 显式分解知识三元组和补全检索增强生成&#xff08;RAG&#xff09;和知识更新 框架设计1. 查询知识图谱2. 处理结果3. 知识图谱演化 CogMG 实现3.1 模型和组件问题分…

.NET 漏洞分析 | 某ERP系统存在SQL注入

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

c++智能指针shared_ptr

文章目录 概念1.shared_ptr1.基本使用2.如何获取原始指针3. 指定删除器 2 使用shared_ptr要注意的问题2.1不要用一个原始指针初始化多个shared_ptr2.2. 避免循环引用 小结 概念 C程序设计中使用堆内存是非常频繁的操作&#xff0c;堆内存的申请和释放都由程序员自己管理。内存…

安装 Docker 环境(通过云平台创建一个实例实现)

目录 1. 删除原有 yum 2. 手动配置 yum 源 3. 删除防火墙规则 4. 保存防火墙配置 5. 修改系统内核。打开内核转发功能。 6. 安装 Docker 7. 设置本地镜像仓库 8.重启服务 1. 删除原有 yum rm -rfv /etc/yum.repos.d/* 2. 手动配置 yum 源 使用 centos7-1511.iso 和 Xi…

Python 语法基础二

7.常用内置函数 执行这个命令可以查看所有内置函数和内置对象&#xff08;两个下划线&#xff09; >>>dir(__builtins__) [__class__, __contains__, __delattr__, __delitem__, __dir__, __doc__, __eq__, __format__, __ge__, __getattribute__, __getitem__, __gt…

深入剖析 Android 网络开源库 Retrofit 的源码详解

文章目录 概述一、Retrofit 简介Android主流网络请求库 二、Retrofit 源码剖析1. Retrofit 网络请求过程2. Retrofit 实例构建2.1 Retrofit.java2.2 Retrofit.Builder()2.2.1 Platform.get()2.2.2 Android 平台 2.3 Retrofit.Builder().baseUrl()2.4 Retrofit.Builder.client()…

OpenAI穿着「皇帝的新衣」;扒了数万条帖子汇总100种AIGC玩法;北美出海的财务避坑指南;我创业「如」有CTO | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; 1. 我扒了 Reddit 论坛数万条帖子&#xff0c;汇总了 GenAI 的 100 种玩法 ChatGPT 已经问世一年半了。这期间诞生了很多大语言模型和生成式人工智能…