《JavaEE》----2.<多线程的简介创建Thread类>

前言:

      大家好,我目前在学习java。我准备利用这个暑假,来复习之前学过的内容,并整理好之前写过的博客进行发布。如果博客中有错误或者没有读懂的地方。热烈欢迎大家在评论区进行讨论!!!

      喜欢我文章的兄弟姐妹们可以点赞,收藏和评论。如果感觉有所收获可以关注我呦。我会持续更新滴,望支持!!!!!!一起加油呀!!!!

语言只是工具,决定你好不好找工作的是你的能力!!!!!

学历本科及以上就够用了!!!!!!!!!!


本篇博客会简单介绍线程、线程的特点、优点、线程的不安全问题、进程与线程的区别、Java中如何进程多线程编程、Thread类。

c++中会讲很多多进程编程,而在Java这样的生态中,并不是很鼓励多进程编程,更鼓励多线程编程。

引入多个进程,目的是为了实现并发编程=>多核cpu的时代。

多进程实现并发编程效果很好,但是多进程编程模型也有明显的缺点:

多进程编程模型的缺点

进程太重量,效率不高。

创建一个进程,销毁一个进程,调度一个进程消耗时间都比较多。

时间消耗在申请资源上。进程是资源分配的基本单位。分配内存操作是一个复杂的操作。

(操作系统内部有一定的数据结构,把空闲的内存分块管理好,当我们进行申请内存的时候,系统就会从这样的数据结构中找到一个大小适合的空闲内存。返回给对应的进程。这里虽然通过此处的数据结构,可以一定程度提高效率,整体来说,管理的空间比较多,相比之下,还是一个耗时操作。)

如果频繁创建/销毁进程时,这个耗时就不能忽视了

为了解决上述问题,就引入了“线程”(Thread)

一、线程也叫做“轻量级进程”

线程不能独立存在,而是要依附于进程,(进程包含线程,可以包含一个或多个)

一个进程最开始至少要有一个线程,这个线程负责完成执行代码的工作。

也可以根据需要,创建出更多的线程,从而使当前实现“并发编程”的效果

每个线程都可以独立执行一些代码。

之前提到的进程调度

是基于“一个进程里只有一个线程”的情况。

实际上,一个进程中,是可以有多个线程的~~每个线程都是可以独立的进行调度的~~

因此以后看到进程的调度,我们就知道,并不是把整个进程进行调度。而是去调度进程里面的每一个线程,每一个线程执行一些逻辑,每一个线程就可以分别在这上面进行调度。每一个线程也有

状态

优先级

上下文

记账信息....

一个进程,可能使用一个PCB表示,也可能使用多个PCB表示,每一个PCB对应到一个线程上,因此每一个线程都有自己的状态、优先级、上下文、记账信息....每一个线程都有这些信息进行辅助调度~~

除此之外,前面谈到的pid,是相同的。内存指针,文件描述符表也是相同的。共用同一份的

线程和线程之间共用同一份pid、内存指针、文件描述符表

二、线程的特点

1.每一个线程都可以独立的去cpu上调度执行

2.同一个进程的多个线程之间共用同一份内存空间和文件资源...

三、线程的优点

创建效率更高

(创建线程的时候,不需要重新申请资源了,直接复用之前已经分配给进程的资源,省去了资源分配的开销,于是创建效率就更高了)

总结:

进程中包含线程,

一个进程由多个PCB共同表示,

每个PCB就用来表示一个线程,

每个线程都有自己的状态、优先级、上下文、记账信息....

每个线程都可以独立去CPU上调度执行,

这些PCB共用了同样的pid、内存指针、文件描述符表

创建线程(PCB)的时候,不需要重新申请资源了,直接复用之前已经分配给进程的资源,省去了资源分配的开销,于是创建效率就更高了

进程是资源分配的基本单位

线程是调度执行的基本单位

一个系统中,可以有很多进程,每个进程都有自己的资源

一个进程中,可以有很多线程,每个线程都能独立调度,共享内存/硬盘资源

四、多线程方式

刚开始创建第一个线程的时候,相当于和进程一起创建,还是需要有一定的开销去申请资源的(这个账是记在进程上面的)后面再创建线程,开销就省下了。

创建多线程方式可以提高效率,但是线程到达一定的数量,效率就无法进一步提升了,反而会因为需要调度的线程太多,使调度的开销更大,反而降低效率。线程多了,也容易产生一定的冲突。

五、线程不安全问题

如果一个线程抛出异常,如果没有妥善处理(要catch住),就容易使整个进程崩溃。此时其他线程也会随之消亡。

六、进程与线程的区别(经典面试题)

1.进程包含线程,一个进程里面可以有一个线程,也可以有多个线程

2.进程和线程,都是用来实现 并发编程 场景的,但是线程比进程更加轻量,更高效

3.同一个进程的线程之间,共用同一份的资源(内存+硬盘),省去了申请资源的开销

4.进程和进程之间,具有独立性,一个进程挂了,不会影响其他进程

   同一个进程的线程和线程之间,可能互相影响,(线程安全问题+线程出现异常)

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

七、Java如何进行多线程编程 

7.1 基本的多线程编程

创建线程的方法

①继承Thread类,重写run方法

线程是操作系统的概念,操作系统提高了一些API,可以操作线程Java针对上述系统API进行了封装(实现跨平台)程序员只需要掌握这一套API就可以了。

Thread类,创建Thread类对象,进一步的就可以操作,系统内部的线程了。使用这个类,创建出一个线程出来。继承“Thread”是Java标准库内置的类,我们直接就能使用。

此处Thread不需要import也能使用,是因为Thread这个类在java.lang包下。

1.创建一个类继承Thread。再重写run方法。这个run方法就是线程的入口方法。入口方法就是代表线程一旦执行起来后,具体要执行哪些逻辑。类似于main方法。

:每个线程都是独立的执行流,每个线程都可以执行一系列的逻辑(代码)一个线程跑起来,就是从它的入口方法开始执行。

类比运行Java程序:就是跑起来一个java进程,这个进程里面至少会有一个线程,主线程的入口方法就是main方法。

2.2.创建一个主线程,也就是在main方法中创建一个Thread的实例,创建好了之后再去调用start方法。

start和run方法的区别的功能描述

//start和run都是Thread的成员

//run只是描述了线程的入口(线程要做什么任务)

//start则是真正调用了系统API,在系统中创建出线程,让线程再调用run

这里的创建线程,实在系统内核里面创建线程。涉及到创建PCB并且加入到内核链表里面,这样创建好的线程就会进一步执行我们的run方法。这样就可以将新的线程创建出来。

此时若在run中有System.out.println(“hello thread”) ;

那么运行程序,就会打印出hello thread


给打印代码加上while(true),死循环,在线程和主线程中一个打印hello thread,另一个打印hello main。运行代码,我们可以发现两边的日志都在交替打印

1.每个线程都是独立执行的逻辑,独立的执行流。

2.从t.start();代码之后,就会兵分两路,并发执行。达到了并发编程的效果,充分的使用了多核cpu资源。


把t.start()改成t.run()。并不会创建新的线程,只有一个主线程,这个主线程依次执行循环,执行完一个循环再执行另一个。

main这个线程是jvm自动创建的,和其他线程相比,没啥特殊的。

一个Java进程中,至少会有一个main线程。

 7.2 查看该进程里的多线程情况

多线程程序运行的时候,可以使用IDEA或者jconsole来观察到该进程里的多线程的情况

IDEA对新手不太友好,以调试模式启动程序,会有一个专门的窗口,查看方法的调用栈,在这里可以看到所有线程的信息。

jconsole来观察到该进程里的多线程的情况(jdk的bin目录中)

1.启动之前,确保idea中的程序已经跑起来了

2.若啥都不显示,可能需要使用管理员方式运行

它会列出当前机器上所运行的所有java进程

使用方法:

1.双击jconsole.exe,出现如下窗口,点击本地进程中你正用IDEA跑起来的进程。点击连接

 2.出现如下窗口,我们要查看线程情况,点击线程

在jconsole,可以看到一个java进程,即使是最简单的,里面也包含了很多线程。

Thread = new MyThread();是自己动手创建的,其他的线程都是JVM自动创建的。一个java进程,启动之后JVM会在后面,默默帮我们做很多事情,比如垃圾回收、资源统计、远程方法调用...

我们只关注两个

1.main是主线程

2.Thread-0是我们创建的线程 ,点进去我们可以看到详细信息。

最主要,我们看堆栈跟踪(也就是线程的“调用栈”):描述了方法的调用关系。

功能:

未来写一些多线程程序的时候,就可以借助这个功能看到该程序实时的运行情况,比如你写的程序“卡死了”。

让while循环慢点(sleep)

在循环体里加上sleep,休眠

Thread.sleep();

输入参数的单位为毫秒。例如:

Thread.sleep(millis:1000);

此语句需要抛出异常,要么往上throws或者进行try,catch。

在当前我们写的线程中(重写run方法),我们必须进行try,catch,因为我们现在是方法重写,如果父类的run方法没有throws,那么子类这个方法也就没法去throws。

而在主线程(main方法中),我们可以进行throws。

sleep是Thread的类(静态)方法。

我们发现两线程都是休眠1000ms,当时间到了之后,这俩线程谁先执行,谁后执行不一定。这个过程可以视为“随机的”。

“对多线程调度顺序的“随机性””

因为操作系统对于多个线程的调度顺序,是不确定的,“随机的”(此处的随机不均等,可能优先级不一样,就算一样是不是均等也很难说,取决于操作系统对于线程调度的模块,调度器的实现),

类方法,类属性 VS(普通) 实例方法,实例属性 

类方法,类属性,直接用类名就可以调用

实例方法,实例属性,需要用类实例化对象,用对象名进行调用。

ps:static历史问题

c语言最初引入了static,以前的操作系统,运行的进程中,专门有一个内存区域,叫做“静态内存区”随着时间发展,静态内存区没有了,后来c就是用static表示其他含义了。

如果static修饰一个全局变量,或者修饰一个方法,表示它的作用域,就在当前.c文件里

如果修饰一个局部变量,那么就表示这个变量的生命周期是跟随整个程序的。

c++中把static又赋予了新的含义,c++引入了类和对象的概念,static就是类的成员,达到“类方法”“类属性”定义效果了(如果新加关键字来表示“类方法”“类属性”不合适,这会导致现有的代码可能产生冲突。如与之前变量名一样,c++要考虑和c兼容,有很多程序使用c,若引入关键字,可能导致现有的代码无法编译。)

java是从c++这边参考过来的,因此java这边也就用static表示类方法,类属性

Python没有这样的历史包袱,因此python直接使用@classmethod这样的方式来表示类方法。

7.3 创建线程的其他写法

②实现Runnable接口,重写run方法 

实现Runnable接口,重写run方法 

这里的内容和之前继承Thread类是一样的。也是描述了线程的入口。

不同的是,这里在main方法中,需要创建Runnable接口的实例化,描述一个任务。

再创建Thread的类的实例化,将Runnable的实例化交给Thread来执行。我们把这个任务放到线程里面去执行。通过t.start();通过这个操作,调用系统api来完成创建线程的工作~

Runnable 

Runnable本身并没有和线程进行联系,单纯的表示一个可运行的任务,这个任务是交给线程负责执行,还是交给其他实体来执行....Runnable本身并不关心。

ps:为什么总是向上转型(java)

Java这个圈子就爱这么写,

如果c++,这里的代码绝对不会写成向上转型。

c++是一个生态,这里的这群人不喜欢向上转型,他们觉得向上转型之后,触发多态,会有额外的运行时开销,不符合c++把性能追求到极致这样的初心。这边能不向上转型就不转型。

如果java,一定写成向上转型的方法。

java也是一个生态。这群人更鼓励使用向上转型,java程序员觉得性能问题不是问题,开发效率大于运行效率。使用向上转型,抽象层次高,代码的使用/理解成本更低。(能向上转型就向上转型。)

多态的本质

封装本质上是让调用者不再了解类实现的细节,从而降低了学习和使用成本。

多态则是在封装基础上更进一步,更是让你不知道当前是啥类。更不用说类的实现细节,你只需要关心它的父类。

就如这里,

只需要看到Runnable runnable(runnable是Thread类型)。而不需要关心后面new 了一个怎么样的runnable

只需要看到Thread t(t是Thread类型),而不需要关心后面new 了 一个怎样的Thread。属于将封装程度更加提高了。学习成本更降低了。

未来在公司中,接触到的各种代码,向上转型,也会非常普遍。

代码比较复杂,容易体会到封装/多态 的意义,

两次创建线程的差别

解耦合。

使用Runnable的写法,和直接继承Thread之间的区别主要是 解耦合。

相互影响越大,我们认为是耦合越高

创建线程,需要两个关键操作:

1.明确线程要执行的任务,

2.调用系统api,创建出线程。

任务本身,不一定和线程 概念 强相关,

这个任务只是单纯的执行一段代码,这个任务是使用单线程,还是多线程执行,还是通过其他方法(信号处理函数 / 协程 / 线程池)都没啥区别。

因此我们可以把任务本身给提取出来~我们将任务和线程之间进行解耦合,解耦合之后,我们随时就可以把任务改成其他方式来去执行。


匿名内部类

在数据结构课程的优先级队列中,我们学到过,能够按照优先级高低,来决定谁先出队列。

如果存的对象,那么谁优先级算高,谁算低呢,此时我们使用Compareble或者Comparator来定义,比较规则.

使用这个的时候,我们就可以使用匿名内部类来进行定义了

③.继承Thread重写run,但是使用匿名内部类.

public class Demo3 {public static void main(String[] args) {Thread t = new Thread(){};}
}

先创建出新的类,这个类的名字是啥,不知道

只知道这个类,是Thread的子类

同时又把这个子类的实例给创建出来了

(不知道这个类名,不影响,因为这个类本身就是只使用一次) 

package thread;public class Demo3 {public static void main(String[] args) {Thread t = new Thread(){@Overridepublic void run() {while (true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};t.start();while (true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

这样看起来更直观。通过匿名内部类创建线程,这个方法本质上和方法①是一样的。只是换了一种写法。匿名内部类这种写法,在当前java中是比较常见的

④.实现Runnable,重写run,也是使用匿名内部类。

package thread;public class Demo41 {public static void main(String[] args) {Runnable runnable = new Runnable() {@Overridepublic void run() {while (true){System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};Thread t = new Thread(runnable);t.start();while (true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

可以看到这样创建所执行的效果和之前也都是一样的,使用这种方式也是匿名内部类的写法。甚至我们可以连Runnable的变量名都可以不要。如下:

package thread;public class Demo42 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {while (true){System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t.start();while (true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

总之匿名内部类在java中很常见,要重点掌握哦!

⑤.基于lambda表达式(最推荐写法)

package thread;public class Demo5 {public static void main(String[] args) {Thread t = new Thread(()->{while (true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();while (true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

lambda(枚举,反射),是一种更简化的语法表示方式,我们称之为“语法糖”

例如for each遍历数组。

for(int i = 0; i<arr.length; i++) 这是一种遍历方式

for(int x : arr)           这也是一种遍历方式,这种更简洁,可以称之为语法糖

相当于匿名内部类的替换写法

       Thread t = new Thread(()->{});

lambda表达式:

这样的写法称之为lambda表达式,其本质上是一个匿名方法。匿名函数(用一次就不用了)。主要用来实现这种“回调函数”的效果。

Java中不允许函数独立存在,必须依托于一个类,(其他语言叫函数function,java这里叫做方法method)

因此这里个代码里面,看起来像是一个单独的函数,本质上是一个函数式接口,还是一个类或者是一个对象。lambda 本质是一个函数式接口(本质上还是没脱离类)。

函数指针:

是指向内存空间的,函数怎么跑到内存中,原因是操作系统,加载 一个可执行程序,创建进程的过程。当写的代码都是一个一个的文件,我们将他们预编译,得到一个exe.文件。还是一个文件,这个时候,函数在文件里,但是当我们双击exe文件,操作系统就会 加载这个exe文件,将exe文件中的指令和数据加载到内存中,构建成一个进程,这个时候,我们写的函数,对应的二进制指令就进入到内存中,这个时候拿指针指向它。 

作用:

1.使用函数指针实现转移表,降低代码的圈复杂度(减少 if else 分支数目)

2.使用函数指针作为回调函数(qsort)

回调函数:

回调函数,不是你主动调用,也不是现在就立即调用,而是把调用的机会交给别人(通常是操作系统,库,框架,别人写的代码)来进行使用,别人会在合适的时机来调用这个函数。

java中可以使用,lambda表达式和匿名内部类来描述这个回调函数。

八、Thread类的其他使用方法

        Thread t1 = new Thread();Thread t2 = new Thread(new MyRunnable());Thread t3 = new Thread("这是我的名字");Thread t4 = new Thread(new MyRunnable(),"这是我的名字");

前两个我们已经见过了,第三个

8.1 Thread(String name)方法

name,在创建线程的时候,我们可以去指定一个name,name不影响线程执行,只是给线程起个名字,后续在调试的时候,比较方便区分。

使用示例如下:

package thread;public class Demo6 {public static void main(String[] args) {Thread t = new Thread(() ->{while (true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"这是新线程");t.start();}
}

运行后,我们在JDK目录中,运行 jconsole.exe文件。

我们发现main线程咋不见了?

因为main执行完了,所以就没有了!!

线程的入口方法执行结束,则这个线程就自动销毁了

对于主线程来说,入口就是main方法

t.start();这个方法,一瞬间就执行完了。当start方法执行完毕之后,紧接着main方法执行结束,那么这个主线程自然没有了,销毁了。线程不是创建了就一直存在,而是执行完了自然就销毁了。

8.2 Tread的几个常见属性

属性                        获取方法

ID                             getId()       ID是线程唯一身份标识,不同线程不会重复。(这个id是                                                          Java给你这个线程分配的,不是系统api提供的线程id,更不                                                    是pcb中的id)

名称                         getName()    

状态                         getState()     就绪,阻塞...等等许多后面我们再去讨论。

优先级                      getPriority()     虽然提供了api可以设置/获取优先级,但是没什么大用,

                                                         从应用程序角度出发,很难察觉出来,优先级带来的差                                                          异,优先级影响到的是系统在微观上进行的调度。

                                                         后面我们再去详细讨论。

是否后台线程           isDaemon()  重点介绍:这个也叫做守护线程(后台线程) ,相对的有                                                       前台线程,如果前台线程没有执行结束,此时整个进程是                                                         一定不会结束的。而后台进程没有执行结束,并不影响整                                                         个进程的结束。默认情况下,一个线程是前台线程,除非                                                         把他手动定义成setDaemon(true) 。

是否存活                  isAlive()      Thread对象的生命周期,要比系统内核中的线程更长一些

                                                    Thread对象还在,内核中的线程已经销毁了这样的情况

                                                    我们可以通过isAlive判定内核线程是不是已经没了

                                                    回调方法执行完毕,线程就没了

是否被中断              isInterrupted()

是否后台线程           isDaemon() 

示例:

package thread;public class Demo6 {public static void main(String[] args) {Thread t = new Thread(() ->{while (true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"这是新线程");//设置t为后台线程t.setDaemon(true);t.start();}
}

运行程序,我们发现什么都没有打印,

改成后台线程之后,主线程飞快执行完了,于是进程结束,t线程还没来得及执行,就完了。

是否存活                  isAlive() 

package thread;public class Demo7 {public static void main(String[] args) {Thread t = new Thread(() ->{System.out.println("线程开始");try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("线程结束");});t.start();System.out.println(t.isAlive());try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(t.isAlive());}}

我们让主线程sleep3000 ,所以当sleep3000完成之后,那么这个线程执行完毕。我们打印       System.out.println(t.isAlive());

 如果线程正在运行,我们调用isAlive() 那么就打印true

如果线程结束,我们调用isAlive() 那么就打印false

true和线程开始,这两条日志,谁先打印,谁后打印不一定,因为线程是并发执行的,并发调度顺序不确定,取决于系统的调度器,(自己尝试,大概率先打印true,因为调用start之后,新的线程被创建也是有一定开销的,创建线程过程中,主线程就执行println)

但是无法排除极端情况,比如主线程正好卡了下,使新线程的日志先打印......

如果在t.start前调用isLive这个时候线程没被创建出来,自然也会打印false

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

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

相关文章

【深度学习】“复杂场景下基于深度学习的卷积神经网络在鸟类多类别识别中的模型设计与性能优化研究“(中)

【深度学习】“复杂场景下基于深度学习的卷积神经网络在鸟类多类别识别中的模型设计与性能优化研究”(中) 大家好 我是寸铁&#x1f44a; 【深度学习】“复杂场景下基于深度学习的卷积神经网络在鸟类多类别识别中的模型设计与性能优化研究”(中)✨ 喜欢的小伙伴可以点点关注 &a…

一个网站搞定Adobe系列软件下载安装,良心网站!

Adobe系列软件几乎是每个办公职场人都会用到的软件&#xff0c;比如PDF&#xff0c;PS&#xff0c;AI&#xff0c;PE&#xff0c;PR等&#xff0c;不管你是设计图片&#xff0c;制作编辑音频还是视频&#xff0c;Adobe都有对应的软件。但是对于大部分用户来说&#xff0c;Adobe…

Linux网络:传输层TCP协议(四)拥塞控制及延迟应答

目录 一、拥塞控制 二、延迟应答 一、拥塞控制 虽然 TCP 拥有滑动窗口这个大杀器机制来根据具体情况对发送的数据大小和速度进行实时控制, 能够高效并且可靠的发送大量的数据. 但是如果在双方建立好连接后的刚开始阶段就发送大量的数据。仍然可能引发一些问题. 因为同一个网…

怎么给PDF文件加密码?关于PDF文件加密的四种方法推荐

怎么给PDF文件加密码&#xff1f;给PDF文件加上密码是保护文件安全的一种重要方法&#xff0c;特别是当需要在不受授权的访问下保护敏感信息时。这个过程不仅仅是简单地设置密码&#xff0c;而是涉及到对文档内容和访问控制的深思熟虑。加密PDF文件可以有效防止未经授权的用户查…

杂谈(杂鱼谈论c语言)——2.大小端字节序

⼤⼩端字节序和字节序判断 当我们了解了整数在内存中存储后&#xff0c;我们调试看⼀个细节&#xff1a; #include <stdio.h> int main() {int a 0x11223344;return 0; } 调试的时候&#xff0c;我们可以看到在a中的 0x11223344 这个数字是按照字节为单位&#xff0c;…

creality ender2的3D打印经验教训

创想云-3D打印模型库-一体化3D打印平台 1.开机后要放一张白纸进行检查&#xff0c;看看打印头立平台的距离&#xff0c;如果太近&#xff0c;会灼烧平台&#xff0c;会造成下面的结果&#xff1a; 2.下载模型&#xff0c;可以在线切片&#xff0c;要看看是否要支撑 没有支撑可…

vulntarget-a

实际部署之后的win7 ip: 192.168.127.128 具体攻击过程如下 win7 扫描服务 使用fscan扫描win 7中的服务以及漏洞 ./fscan -h 192.168.127.128 扫出来一个ms17-010以及通达oa的漏洞&#xff0c;既然有永恒之蓝的&#xff0c;直接上MSF就行了 msf6 > search ms17-010 msf6…

Bouncy Castle集成SM2与SM3

在Bouncy Castle库中&#xff0c;SM2和SM3是两种分别用于非对称加密和数字签名的密码算法&#xff0c;它们也可以结合使用&#xff0c;形成一种高安全性的加密签名方案&#xff0c;即SM2withSM3。以下是对SM2SM3的详细解释&#xff1a; 一、SM2算法 SM2是一种由中国国家密码管…

学前教育优化算法,原理详解,MATLAB代码免费获取

学前教育优化算法&#xff08;Preschool Education Optimization Algorithm&#xff0c;PEOA)是一种受学前教育过程中孩童的活动行为启发而提出的元启发式优化算法。学前教育在儿童的早期发展中起着至关重要的作用&#xff0c;并为他们未来的学习旅程奠定基础。作为幼儿学习者发…

【JavaScript】详解Day.js:轻量级日期处理库的全面指南

文章目录 一、Day.js简介1. 什么是Day.js&#xff1f;2. 安装Day.js 二、Day.js的基本用法1. 创建日期对象2. 格式化日期3. 解析日期字符串4. 操作日期5. 比较日期 三、Day.js的高级功能1. 插件机制2. 国际化支持 四、实际应用案例1. 事件倒计时2. 日历应用 在JavaScript开发中…

Python | ValueError: could not convert string to float: ‘example’

Python | ValueError: could not convert string to float: ‘example’ 在Python编程中&#xff0c;类型转换是一个常见的操作。然而&#xff0c;当尝试将一个字符串转换为浮点数时&#xff0c;如果字符串的内容不是有效的浮点数表示&#xff0c;就会遇到“ValueError: could…

labview四字节转浮点数

1.labview四字节转浮点数 2.Labview怎么把串口接收到的数据转换成浮点数&#xff1f; Labview怎么把串口接收到的数据转换成浮点数&#xff1f;

如何跨越 LangChain 应用研发的最后一公里

说 [LangChain] 是现在最流行的 AI 应用开发框架&#xff0c;应该没有人出来反对吧。LangChain 的出现极大地简化了基于大型语言模型&#xff08;LLM&#xff09;的 AI 应用构建难度&#xff0c;如果把 AI 应用比作一个人的话&#xff0c;那么 LLM 相当于这个人的“大脑”&…

基于vue-grid-layout插件(vue版本)实现增删改查/拖拽自动排序等功能(已验证、可正常运行)

前端时间有个需求&#xff0c;需要对33&#xff08;不一定&#xff0c;也可能多行&#xff09;的卡片布局&#xff0c;进行拖拽&#xff0c;拖拽过程中自动排序&#xff0c;以下代码是基于vue2&#xff0c;可直接运行&#xff0c;报错可评论滴我 部分代码优化来自于GPT4o和Clau…

78.WEB渗透测试-信息收集-框架组件识别利用(2)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;77.WEB渗透测试-信息收集-框架组件识别利用&#xff08;1&#xff09; shiro&#xff1a;…

支持向量机 及其分类案例详解(附Python 代码)

支持向量机分类器预测收入等级 我们将构建一个支持向量机&#xff08;SVM&#xff09;分类器&#xff0c;以预测一个人基于14个属性的收入等级。我们的目标是判断收入是否高于或低于每年$50,000。因此&#xff0c;这是一个二元分类问题。我们将使用在此处可用的人口普查收入数…

JDBC基础

目录 一、JDBC概述 二、JDBC搭建 1.注册JDBC驱动程序 2.建立与数据库连接 3.获得Satement执行sql语句 4.关闭与数据库的链接通道 三、PreparedStatement和Statement 1、代码的可读性和可维护性 2、最重要的一点是极大地提高了安全性 四、结果集处理 一、JDBC概述 JD…

c语言指针2

文章目录 一、void * 指针二、const关键字1.const修饰变量2.const修饰指针变量2. 1 const放在*的右边2. 2 const放在*的左边2. 3 总结 三、指针的运算3. 1指针的加减运算3. 2 指针 - 指针3. 3 指针的关系运算 四、野指针4. 1 什么叫野指针&#xff1f;4. 1 野指针的成因4.1.1 指…

Poetry入门教程

以前使用模块管理和虚拟环境为pip和Virtualenv组合&#xff0c;随着Rasa、Dify等开源项目逐步使用Poetry模块管理&#xff0c;也开始尝试使用Poetry。本文简要介绍Poetry入门操作。 1.Poetry安装 可参考Poetry官网[1]推荐的安装方式&#xff1a; 通过Windows的Powershell如下…

C++编程: 使用 Nanomsg 进行 PUB-SUB 模式基准测试

文章目录 0. 引言1. Nanomsg简介1.1 可扩展性协议类型1.2 支持的传输机制1.3 NanoMsg 架构与实现 2. PUB-SUB 模式基准测试 0. 引言 Nanomsg 作为一款高性能的通信库&#xff0c;支持多种消息传递模式&#xff0c;其中包括 PUB-SUB&#xff08;发布-订阅&#xff09;。 本篇文…