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

3. 多线程(1) --- 创建线程,Thread类

文章目录

  • 前言
  • 1. API
  • 2. 创建线程
    • 2.1. 继承 Thread类
    • 2.2. 实现 Runnable 接口
    • 2.3. 匿名内部类
    • 2.4. lambda
    • 2.5.其他方法
  • 3. Thread类及其常见的方法和属性
    • 3.1. Thread 的常见构造方法
    • 3.2. Thread 的常见属性
    • 3.3. start() --- 启动一个线程
    • 3.4. 中断一个线程
    • 3.5. 等待线程
    • 3.6. 休眠当前线程


前言

上一节课,我们主要引出了线程,以及线程和进程的联系与区别,从这篇博客开始,我们开始编写代码了,Let’s go!


1. API

线程是操作系统提供的概念,操作系统系统提供一些API供程序员使用。什么是 API 呢?
API (Aoolication Programming Interface) 应用程序编程接口,简单来说,就是别人写了一些类 / 函数,可以直接拿过来使用。
来源:api 的来源很广泛,例如 标准库,第三方库,其他的各种开源项目,甚至是在工作中,隔壁项目给你提供的代码。
并且呀,操作系统提供的原生线程 api 是 C语言的,并且不同的操作系统的线程 api 是不一样的。
为此, Java 对上述内容统一封装,提供Thread (标准库中的类)


2. 创建线程

2.1. 继承 Thread类

class MyThread extends Thread{@Overridepublic void run() {while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("Hello thread!");}}
}
public class Demo1 {public static void main(String[] args) throws InterruptedException {// 方法一:创建一个 MyThread 类继承 Thread,重写 run 方法Thread thread = new MyThread();thread.start();while (true){System.out.println("Hello main");Thread.sleep(1000);}}
}

上面便是创建一个 MyThread 类 继承 Thread,重写run方法的形式,下面我们来分别说一下。

class MyThread extends Thread{@Overridepublic void run() {while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("Hello thread!");}}
}

这段代码中,是我们自己利用JVM自带的Thread类实现的一个MyThread,并且重写了run方法,当我们调用线程的时候,就直接运行run方法的内容了,无需调用。
为什么不需要调用 run()方法呢?
因为 run() 相当于 回调函数
例如在Java的数据结构中,优先级队列执行比较规则,如果使用 Comparator 接口,方式就是 compare,如果使用 Comparable接口,方法就是 compareTo,我们没有调用这个函数,他就自己就调用了,这就是回调函数

Thread.sleep(1000);

这个方法是Thread 的静态方法,参数是毫秒,效果就是,当前的线程放弃 CPU 资源,休息一会,时间过了,再执行。为什么要进行这个操作呢,因为如果不休眠的话,那么 这两个线程会一直吃 CPU 的资源,可能会造成 电脑的卡顿,并且让 线程休息一下,也便于我们观察现象。
这个方法会抛出 InterruptedException 异常,需要我们 使用 try catch 进行捕获。因为 run()方法没有 throws ,所以就不能向上抛出异常,只能使用 try catch,这个跟下面的main方法中的不太一样。

Thread thread = new MyThread();

这个就创建了一个线程 thread,和main 线程一起执行。

thread.start();

thread 线程 开始执行,此处为什么不调用 thread.run(),而是 thread.start()

 		while (true){System.out.println("Hello main");Thread.sleep(1000);}

这段代码是main线程中执行的逻辑,跟上面 thread 几乎一模一样,就是为了观察效果的。
在这里插入图片描述
效果显而易见,我们发现 Hello main 和 Hello thread!,运行的过程中,是轮番打印的,并且没有规律。
在这说一下如果调用的是 thread.run()而不是 thread.start(),会出现什么现象。

public class Demo01 {public static void main(String[] args) throws InterruptedException {// 方法一:创建一个 MyThread 类继承 Thread,重写 run 方法Thread thread = new MyThread();//thread.start();thread.run();while (true){System.out.println("Hello main");Thread.sleep(1000);}}
}

运行情况:
在这里插入图片描述
相当于thread线程就没有运行,因此不要这样写代码。

2.2. 实现 Runnable 接口

class MyRunnable implements Runnable{@Overridepublic void run() {while (true){try {System.out.println("Hello thread!");Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class Demo02 {public static void main(String[] args) throws InterruptedException {Runnable runnable = new MyRunnable();Thread thread = new Thread(runnable);thread.start();while (true){System.out.println("Hello main!");Thread.sleep(1000);}}
}
Runnable runnable = new MyRunnable();

这个就是一个任务,一段要执行的逻辑,最终还是要通过 Thread ,才能创建真正的线程,那为什么这样子写呢?或者就是这样子写有什么好处?
可以做到解耦合
意思就是,让要执行任务本身这个事情,和 线程这个事可以 关联没有那么大,从而后续如果要变更代码 (比如不通过线程执行这个任务,而是通过其他方法…),那么采用 Runnable 这样的方案,代码的修改就会更简单了。
与此相关的还有一个名词高内聚
例如,写了一个项目,有很多代码,有很多文件,也有很多类,以及很多的逻辑,其中把有关联的各种代码,放到一起,只要把某个功能逻辑相关的东西,都放到一起,那么就称之为高内聚
反之,如果是某个功能的代码,东一块,西一块的,那么就是低内聚
因此么,我们在写代码的时候,通常要做到高内聚,低耦合
现象跟上面的情况差不多。
在这里插入图片描述

2.3. 匿名内部类

利用 Thread 来使用 匿名内部类

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

这个代码做了三个事情:

  1. 创建了一个 Thread 的子类,子类叫什么名字?不知道,他是个匿名!
  2. { } 里面就可以编写子类的定义代码,子类里有哪些属性,要有哪些方法,重写父类哪些方法…都可以往里面写。
  3. 创建了这个匿名内部类的实例,并且把实例的引用赋值给了 t。

同理,下面 Runnable 也是这个意思。
现象如下图所示:

在这里插入图片描述

利用 Runnable 来实现 匿名内部类

public class Demo04 {public static void main(String[] args) throws InterruptedException {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 thread = new Thread(runnable);thread.start();while (true){System.out.println("Hello main!");Thread.sleep(1000);}}

在这里插入图片描述
使用匿名内部类的好处就是:
少定义一些类。
一般如果某个代码是 “一次性的”,就可以使用匿名内部类的写法。

2.4. lambda

其实 lambda 表达式的本质,就是回调函数,使用 () -> {}

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

注意。没有 Runnable 的形式。
咱们上课主要就是使用 lambda 表达式的形式来创建线程。

2.5.其他方法

不止上面的四种方法,还有两种,一个是线程池,另外一种是实现Callable接口,等到后面我们再说。


3. Thread类及其常见的方法和属性

3.1. Thread 的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target,String name)使用 Runnable 对象创建线程对象,并命名
【了解】Thread(ThreadGroup group,Runnable target)线程可以被用来分组管理,分好的组即为线程组,这个目前了解即可

其中第一个和第二种方法,都已经演示过了,直接演示第四个吧。

public class Demo06 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while (true){System.out.println("Hello t1");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"t1");Thread t2 = new Thread(()->{while (true){System.out.println("Hello t2");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"t2");t1.start();t2.start();while (true){System.out.println("Hello main");Thread.sleep(1000);}}
}

此时,我们打开JDK自带的软件 jconsole (在JDK的bin目录下面),
在这里插入图片描述
其中,没有用红色框起来的都是JVM自带的线程,跟咱们没有关系。
其中t1,t2都是我们定义好的线程名称,如果没有定义名字的话,会是什么样子的呢?
在这里插入图片描述
是这个样子的。

3.2. Thread 的常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
  • ID 是线程的唯一标识,不同线程不会重复
  • 名称是各种调试工具用到的
  • 状态表示线程当前所处的一个情况,下面我们会进一步说明
  • 优先级高的线程理论上来说更容易被调度到
  • 关于后台线程,需要记住:JVM会在一个进程的所有非后台线程结束后,才会结束运行
  • 是否存活,一个简单的理解,就是run方法是否运行结束了
  • 线程中断问题,下面我们再说。

在这又得解释一些名词,并且再加上一些代码。
第一个如果获取到线程的名字。
使用这个类中的函数 Thread.currentThread().getName()

public class Demo07 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while (true) {System.out.println("hello " + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();while (true){System.out.println("hello main");Thread.sleep(1000);}}
}

第二个什么是isDaemon()?
表格上写的是后台线程,但是如果按照英语翻译的话是 守护线程。翻译成守护线程,但是要把他理解成后台线程。怎么理解呢?
具体例子:中秋节亲戚们聚餐吃饭,等到一家子人,全部坐齐,举杯碰一个之后,然后开始吃饭,但是么,我下午还有课,我就哐哐地吃饭,我吃饱喝足要去上课了,剩余的亲戚都没有吃完呢,但是我就要出门了。
在上述的过程中,把房间看成程序,把自己一个人闷头吃饭看做一个线程(t1),把剩余的人一起吃完饭,看成另一个线程(t2),对于t1来说,不管t2是否完成,t1一结束,那么程序必须关闭,那么此时就把t2设置为后台线程,也就是说,不管大家吃完了没有(t2是否结束),我吃完饭,房间门必须要打开(程序就要结束了),我就要走了。

public static void main(String[] args) {Thread t1 = new Thread(()->{while (true){System.out.println("全家人一直在吃");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread t2 = new Thread(()->{for (int i = 0;i<3;i++){System.out.println("我也在吃,但是吃完饭就要结束这个线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.setDaemon(true);t1.start();t2.start();}

结果:
在这里插入图片描述

如果没有setDemon(true),那会发生什么呢?

public static void main(String[] args) {Thread t1 = new Thread(()->{while (true){System.out.println("全家人一直在吃");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread t2 = new Thread(()->{for (int i = 0;i<3;i++){System.out.println("我也在吃,但是吃完饭就要结束这个线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});//t1.setDaemon(true);t1.start();t2.start();}

在这里插入图片描述
如果按照第二种的话,意思就是说,我都已经吃饱喝足了,我还是不走,线程 t2 还要在程序中等待。
获得 线程的名字 在上面的代码已经使用过了,在这都不写了。

3.3. start() — 启动一个线程

 public static void main(String[] args) {Thread t = new Thread(() -> {System.out.println("hello thread");});t.start();}

这个是个很简单的代码,大家应该都可以看懂,如果我们这样写代码,看看会出现什么情况

 public static void main(String[] args) {Thread t = new Thread(() -> {System.out.println("hello thread");});t.start();t.start();}

在这里插入图片描述
由此我们可以看出,线程不能重复start。
通过我们查看 线程 start的。
在这里插入图片描述

3.4. 中断一个线程

我们使用两种方式来中断线程,方案一:使用变量 isFinished,方案二:使用interrupt方法。
方案一:使用变量 isFinished。

public class Demo10 {public static boolean isFinshed = false;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (!isFinshed){System.out.println("Hello Thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();Thread.sleep(3000);isFinshed = true;}
}

上面代码意思:t线程每隔1秒,打印一个 “Hello Thread!”,3秒以后,不再打印。
这个是通过 isFinished 变量来确定的。在这提一个问题:

public static boolean isFinshed = false;

这个代码可不可以写在main函数里面?
答案是不行的。

public class Demo10 {//public static boolean isFinshed = false;public static void main(String[] args) throws InterruptedException {boolean isFinshed = false;Thread t = new Thread(() -> {while (!isFinshed){System.out.println("Hello Thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();Thread.sleep(3000);isFinshed = true;}
}

在这里插入图片描述
为什么呢?

在第二种写法中(isFinished变量在main方法中),触发了变量捕获
lambda是回调函数,执行时机,是很久以后才会被执行的(操作系统真正创建出线程之后),很有可能,后续线程都创建好了,当前main这里的方法都执行完了,对应的isFinished 都已经销毁了,为了解决这个问题,Java的做法是,把被捕获的变量拷贝一份,拷贝到 lambda im,外面的变量是否销毁,并不影响 lambda方面里面的变量。
但是还是没有解决问题,拷贝,意味着,这样的变量就不适合进行修改,修改一方,另外一方不会随之变化,本质上是两个变量,这种一边变化,一边又不变,可能会给程序员带来更多的疑惑,那么Java大佬直接压根就不让这个变量进行修改,这就是变量捕获操作。

那么为什么第一种都可以了?
因为第一种写法把 isFinished 这个变量改写成 成员变量,此时就不再是 “变量捕获” 语法,
而是切换成 **“内部类访问外部类的成员”**语法。
lambda 本质上是 函数式接口,相当于一个内部类,isFinished 变量本身是外部类的成员,内部类本来就能够访问外部类的成员。并且 成员变量的生命周期,也是让 GC (垃圾回收)管理的,在 lambda 里面不担心变量生命周期失效的问题,那么就不必拷贝,也就不必限制 final 之类。

下面我们使用 Interrupt 方法来终止线程。

public class Demo11 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()){System.out.println("Hello Thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t 线程 结束");});t.start();Thread.sleep(3000);System.out.println("main 线程 尝试 阻塞 t 线程");t.interrupt();}
}

结果:
在这里插入图片描述
其中:
Thread.currentThread().isInterrupted()判断线程时候被终止了 (其实是判断 Thread 里的 boolean 变量的值)
t.interrput()主动去进行终止的 (修改这个 Boolean 变量的值)
当然,除了设置Boolean 变量 (标志位) 之外,还能够唤醒 像 sleep 这样的阻塞方法。
如果我们把 throw new RuntimeException(e);
这个代码给注释掉,看看会发生什么情况。

public class Demo11 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()){System.out.println("Hello Thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {//throw new RuntimeException(e);break;}}System.out.println("t 线程 结束");});t.start();Thread.sleep(3000);System.out.println("main 线程 尝试 阻塞 t 线程");t.interrupt();}
}

结果:在这里插入图片描述
发生上面的代码的原因,是 sleep 捣鬼。
正常来说,调用 interrput 方法就会修改 isInterrputted 方法内部的标志位 设置为 true
由于上述代码,是把 sleep 给 唤醒了
这种唤醒的情况下,sleep 就会在唤醒 之后,把 isInterruptted 标志位 设置为 false
因此在这样的情况下,如果继续执行到循环的条件判定,就会能够继续执行。

3.5. 等待线程

多个线程之间 是并发执行的,也是随机调度的。
站在 程序员的角度来说,咱们不喜欢随机的东西的。
join 能够要求,多个线程之间结束的 先后顺序
sleep 可以通过 休眠的时间,来控制线程结束的顺序的,但是有的时候,这样的设定是并不科学的。
有的时候,希望 t 先 结束,main 就可以紧跟着结束,此时通过设置时间的方式,不一定靠谱。

public class Demo12 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{for (int i = 0;i<3;i++){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t 线程结束");});t.start();t.join();System.out.println("main 线程结束");}
}

结果:
在这里插入图片描述
此时上面代码的逻辑就是,t每休息1s,打印一次 hello thread,执行三次,main一直等待。

join 也提供了带有时间参数的版本。意思就是等待的最多时间

public class Demo12 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{for (int i = 0;i<5;i++){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t 线程结束");});t.start();t.join(3000);System.out.println("main 线程结束");}
}

在这里插入图片描述
此时,t.joiin(3000)意思就是:如果在 30000ms之内,t 线程结束了,那么此时 join 立即继续执行 (不会等满 3000ms),如果超过3000ms,t线程还没结束,那么此时 join() 往下执行,就不等 t 线程了。
这种带有超时时间版本的等待,才是是更加科学的做法。计算机中,尤其是和网络通信相关的逻辑,一般都是需要超时时间的#

3.6. 休眠当前线程

这个sleep方法我们之前一直在用,在这我们就不展开细讲了
在这里插入图片描述


下节课,我们就要讲述线程的状态和线程不安全的情况了,我们不见不散。

相关文章:

3. 多线程(1) --- 创建线程,Thread类

文章目录 前言1. API2. 创建线程2.1. 继承 Thread类2.2. 实现 Runnable 接口2.3. 匿名内部类2.4. lambda2.5.其他方法 3. Thread类及其常见的方法和属性3.1. Thread 的常见构造方法3.2. Thread 的常见属性3.3. start() --- 启动一个线程3.4. 中断一个线程3.5. 等待线程3.6. 休眠…...

简单的jmeter数据请求学习

简单的jmeter数据请求学习 1.需求 我们的流程服务由原来的workflow-server调用wfms进行了优化&#xff0c;将wfms服务操作并入了workflow-server中&#xff0c;去除了原来的webservice服务调用形式&#xff0c;增加了并发处理&#xff0c;现在想测试模拟一下&#xff0c;在一…...

智能水文:ChatGPT等大语言模型如何提升水资源分析和模型优化的效率

大语言模型与水文水资源领域的融合具有多种具体应用&#xff0c;以下是一些主要的应用实例&#xff1a; 1、时间序列水文数据自动化处理及机器学习模型&#xff1a; ●自动分析流量或降雨量的异常值 ●参数估计&#xff0c;例如PIII型曲线的参数 ●自动分析降雨频率及重现期 ●…...

民宿酒店预订系统小程序+uniapp全开源+搭建教程

一.介绍 一.系统介绍 基于ThinkPHPuniappuView开发的多门店民宿酒店预订管理系统&#xff0c;快速部署属于自己民宿酒店的预订小程序&#xff0c;包含预订、退房、WIFI连接、吐槽、周边信息等功能。提供全部无加密源代码&#xff0c;支持私有化部署。 二.搭建环境 系统环境…...

计算机网络掩码、最小地址、最大地址计算、IP地址个数

一、必备知识 1.无分类地址IPV4地址网络前缀主机号 2.每个IPV4地址由32位二进制数组成 3. /15这个地址表示网络前缀有15位&#xff0c;那么主机号32-1517位。 4.IP地址的个数&#xff1a;2**n (n表示主机号的位数) 5.可用&#xff08;可分配&#xff09;IP地址个数&#x…...

Mac中配置vscode(第一期:python开发)

1、终端中安装 xcode-select --install #mac的终端中安装该开发工具 xcode-select -p #显示当前 Xcode 命令行工具的安装路径注意&#xff1a;xcode-select --install是在 macOS 上安装命令行开发工具(Command Line Tools)的关键命令。安装的主要组件包括&#xff1a;C/C 编…...

软件项目体系建设文档,项目开发实施运维,审计,安全体系建设,验收交付,售前资料(word原件)

软件系统实施标准化流程设计至关重要&#xff0c;因为它能确保开发、测试、部署及维护等各阶段高效有序进行。标准化流程能减少人为错误&#xff0c;提升代码质量和系统稳定性。同时&#xff0c;它促进了团队成员间的沟通与协作&#xff0c;确保项目按时交付。此外&#xff0c;…...

计算机网络--路由表的更新

一、方法 【计算机网络习题-RIP路由表更新-哔哩哔哩】 二、举个例子 例1 例2...

CDN防御如何保护我们的网络安全?

在当今数字化时代&#xff0c;网络安全成为了一个至关重要的议题。随着网络攻击的日益频繁和复杂化&#xff0c;企业和个人都面临着前所未有的安全威胁。内容分发网络&#xff08;CDN&#xff09;作为一种分布式网络架构&#xff0c;不仅能够提高网站的访问速度和用户体验&…...

matlab离线安装硬件支持包

MATLAB 硬件支持包离线安装 本文章提供matlab硬件支持包离线安装教程&#xff0c;因为我的matlab安装的某种原因&#xff08;破解&#xff09;&#xff0c;不支持硬件支持包的安装&#xff0c;相信也有很多相同情况的朋友&#xff0c;所以记录一下我是如何离线安装的&#xff…...

使用virtualenv创建虚拟环境

下载 virtualenv pip install virtualenv 创建虚拟环境 先进入想要的目录 一般为 /envs virtualenv 文件名 --python解释器的版本 激活虚拟环境 .\虚拟项目的文件夹名称\Scripts\activate 退出虚拟环境 deactivate...

Java链表

链表(Linked List)是一种线性数据结构&#xff0c;它由一系列节点组成&#xff0c;每个节点包含两部分&#xff1a;一部分为用于储存数据元素&#xff0c;另部分是一种引用(指针),指向下一个节点。 这种结构允许动态地添加和删除元素&#xff0c;而不需要像数组那种大规模的数…...

Zero to JupyterHub with Kubernetes 下篇 - Jupyterhub on k8s

前言&#xff1a;纯个人记录使用。 搭建 Zero to JupyterHub with Kubernetes 上篇 - Kubernetes 离线二进制部署。搭建 Zero to JupyterHub with Kubernetes 中篇 - Kubernetes 常规使用记录。搭建 Zero to JupyterHub with Kubernetes 下篇 - Jupyterhub on k8s。 官方文档…...

解决 Tomcat 跨域问题 - Tomcat 配置静态文件和 Java Web 服务(Spring MVC Springboot)同时允许跨域

解决 Tomcat 跨域问题 - Tomcat 配置静态文件和 Java Web 服务&#xff08;Spring MVC Springboot&#xff09;同时允许跨域 Tomcat 配置允许跨域Web 项目配置允许跨域Tomcat 同时允许静态文件和 Web 服务跨域 偶尔遇到一个 Tomcat 部署项目跨域问题&#xff0c;因为已经处理…...

在C语言中使用伪终端与bash交互

了解伪终端概念&#xff1a; 伪终端&#xff08;PTY&#xff09;由一对设备组成&#xff1a;主设备&#xff08;master&#xff09;和从设备&#xff08;slave&#xff09;。数据写入主设备会出现在从设备&#xff0c;反之亦然。这允许一个进程通过主设备与另一个进程&#xff…...

阿里云 人工智能与机器学习

阿里云的 人工智能&#xff08;AI&#xff09;与机器学习&#xff08;ML&#xff09; 服务为企业提供了全面的AI解决方案&#xff0c;帮助用户在多个行业实现数据智能化&#xff0c;提升决策效率&#xff0c;推动业务创新。阿里云通过先进的技术和丰富的工具&#xff0c;支持用…...

HTML 显示器纯色亮点检测工具

HTML 显示器纯色亮点检测工具 相关资源文件已经打包成html等文件&#xff0c;可双击直接运行程序&#xff0c;且文章末尾已附上相关源码&#xff0c;以供大家学习交流&#xff0c;博主主页还有更多Html相关程序案例&#xff0c;秉着开源精神的想法&#xff0c;望大家喜欢&#…...

【漏洞分析】UDF提权漏洞——CVE-2016-6662-MySQL ‘malloc_lib’变量重写命令执行

0x00 前言 最近在做渗透笔记&#xff0c;其中有一个靶机在getshell后&#xff0c;需要进行提权。发现靶机使用root启动的mysql服务&#xff0c;那么尝试使用UDF提权。于是在提权成功后&#xff0c;花了一天时间特意搜了一下整个UDF提权的漏洞原理和利用&#xff0c;加深理解。…...

Mybatis(day09)

Mybatis基础操作 功能列表&#xff1a; 查询 根据主键ID查询 条件查询新增更新删除 根据主键ID删除 根据主键ID批量删除 准备 实施前的准备工作&#xff1a; 准备数据库表创建一个新的 springboot 工程&#xff0c;选择引入对应的起步依赖&#xff08;mybatis、mysql 驱动、…...

模式识别与机器学习 | 十一章 概率图模型基础

隐马尔科夫模型&#xff08;Hidden Markov Model,HMM&#xff09; HMM是建模序列数据的图模型 1、第一个状态节点对应一个初始状态概率分布 2、状态转移矩阵A, 3、发射矩阵概率B 4、对特定的&#xff08;x,y&#xff09;的联合概率可以表示为 α递归计算——前向算法β递归…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...