11 多线程详解

news/2025/1/15 23:38:48/文章来源:https://www.cnblogs.com/you-ni/p/18673910

线程简介

程序:程序就像一堆写好的指令和数据放在一起,它是静止的,不会自己动起来。

进程(Process):进程是把程序真正运行起来的过程,它是动态的,系统会给它分配各种资源,比如内存等。

线程(Thread)一个进程里通常会有好几个线程,最少也得有一个,不然进程就没啥用了。线程是 CPU 安排干活和实际执行任务的小单位

注意事项:很多时候说的多线程其实是模拟出来的,真正的多线程得有多个 CPU(就是多核,像服务器那种)。要是模拟的多线程,只有一个 CPU 的时候,同一瞬间 CPU 只能执行一个代码,不过因为它切换得特别快,就好像是同时在执行好多事一样,但其实是一种错觉。

  • 线程就像独立的做事通道。
  • 程序运行时,就算你不创建,后台也有主线程、gc 线程等好多线程。
  • main () 是主线程,是程序开始运行的地方,负责执行整个程序。
  • 一个进程里开了多个线程,啥时候运行由调度器安排,调度器和操作系统关系密切,顺序不能随便改。
  • 多个线程操作同一份资源会抢资源,得用并发控制。
  • 用线程会有额外花费,像 CPU 安排线程的时间和并发控制的花费。
  • 每个线程在自己的工作内存里交流,内存管不好会让数据乱套。

线程实现(重点)

  • 线程创建

image-20250115152709464

继承Thread类

  1. 自定义线程类继承Thread
  2. 重写run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
public class MyThread extends Thread {// 重写Thread类的run方法,run方法里是线程要执行的任务@Overridepublic void run() {// 这里写线程要执行的具体任务for (int i = 0; i < 20; i++) {System.out.println("自定义线程1正在运行:" + i);}}
}public class ThreadExample {public static void main(String[] args) {// 创建自定义线程的对象MyThread myThread = new MyThread();// 启动线程,注意不是调用run方法,而是调用start方法,调用start方法后会自动调用run方法myThread.start();}
}

首先,我们定义了一个 MyThread 类,它继承自 Thread 类。在 MyThread 类中,重写了 run 方法。run 方法是线程的执行体,当线程启动后,会自动调用这个方法执行具体的任务。

run 方法中,我们使用了一个 for 循环打印了一些信息,这就是这个线程要执行的具体任务。

然后,在 ThreadExample 类的 main 方法中,创建了 MyThread 类的对象 myThread

最后,调用 myThread.start() 方法来启动线程。这里要注意的是,不能直接调用 run 方法,因为直接调用 run 方法只是普通的方法调用,而不会开启新的线程,只有调用 start 方法,Java 虚拟机才会为这个线程分配资源并启动线程,进而调用 run 方法。

实现Runnable接口(推荐使用)

  1. 定义MyRunnable类实现Runnable接口
  2. 实现run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
public class MyRunnable implements Runnable {// 实现 Runnable 接口的 run 方法,该方法包含线程要执行的任务@Overridepublic void run() {// 这里是线程要执行的具体任务for (int i = 0; i < 10; i++) {System.out.println("自定义线程正在运行:" + i);}}
}public class RunnableExample {public static void main(String[] args) {// 创建 MyRunnable 类的实例MyRunnable myRunnable = new MyRunnable();// 创建 Thread 对象,并将 MyRunnable 实例作为参数传递给它Thread thread = new Thread(myRunnable);// 启动线程thread.start();}
}

首先,我们创建了一个名为 MyRunnable 的类,它实现了 Runnable 接口。在 MyRunnable 类中,重写了 run 方法。run 方法是 Runnable 接口中唯一的抽象方法,当线程启动后,它将执行 run 方法中所包含的任务。

run 方法中,我们使用了一个 for 循环,从 0 到 9 打印信息,这就是该线程要执行的具体任务。

然后,在 RunnableExample 类的 main 方法中,创建了 MyRunnable 类的实例 myRunnable

接着,我们创建了一个 Thread 对象 thread,并将 myRunnable 作为参数传递给它。这是因为 Thread 类的构造函数可以接受一个 Runnable 接口的实例,这样 thread 线程启动后,会执行 myRunnablerun 方法。

最后,调用 thread.start() 方法启动线程。

这种实现 Runnable 接口的方式是 Java 中创建线程的一种常用方式,与继承 Thread 类相比,实现 Runnable 接口的优势在于避免了 Java 单继承的限制,使代码更加灵活,因为一个类可以实现多个接口,但只能继承一个父类。

另外,使用 Runnable 接口可以方便地将任务和线程对象分离,在需要将任务共享给多个线程时,只需要创建多个 Thread 对象,并将同一个 Runnable 实例传递给它们,就可以让多个线程执行相同的任务。例如,如果我们想让多个线程执行相同的 run 方法,可以这样做:

public class RunnableExample {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread1 = new Thread(myRunnable);Thread thread2 = new Thread(myRunnable);thread1.start();thread2.start();}
}

上述代码创建了两个 Thread 对象 thread1thread2,它们都执行 myRunnablerun 方法,实现了多个线程执行相同任务的目的。

实现Callable接口(了解即可)

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行:Future result1 = ser.submit(t1);
  6. 获取结果:boolean r1 = result.get()
  7. 关闭服务:ser.shutdownNow()
  • Lamda 表达式

Lambda 表达式是 Java 8 引入的一个新特性,它可以让你用一种简洁的方式表示一个匿名函数。就像是一种简洁的代码书写方式,让你能更方便地编写一些功能简单的函数,而不用像以前那样,为了一个小功能去创建一个完整的类或方法。

通常的格式(参数列表) -> {函数体}。比如说,如果你想定义一个对两个整数求和的函数,传统的写法可能要写一个完整的方法,但是用 Lambda 表达式可以写成 (int a, int b) -> {return a + b;}

Lambda 表达式的优点

  • 简洁性:能大大减少代码量,让代码看起来更简洁,读起来也更清晰,尤其是对于那些功能比较简单的函数,以前可能需要写好几行,现在用 Lambda 就能在一行里搞定。
  • 功能性:可以作为参数传递给其他方法,在使用 Java 的一些函数式接口(只有一个抽象方法的接口)时特别有用。比如 Java 中的 Runnable 接口,以前你可能需要创建一个类来实现它,现在可以直接使用 Lambda 表达式。

例如,使用 Lambda 表达式创建一个 Runnable 线程,传统写法是:

// 匿名内部类,没有类的名称,必须借助接口或者父类。原本Runnable接口使用MyRunnable类来实现。
Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("线程正在运行");}
};

使用 Lambda 表达式后可以写成:

Runnable runnable = () -> {System.out.println("线程正在运行");
};

下面是lambda大致推导过程(供参考):

public class LambdaDemo {// 3.静态内部类:static修饰,类里面定义类static class Like2 implements ILike{@Overridepublic void lambda() {System.out.println("i like lambda2");}}public static void main(String[] args) {ILike like = new Like();like.lambda();like = new Like2();like.lambda();// 4.局部内部类:方法里面定义类class Like3 implements ILike{@Overridepublic void lambda() {System.out.println("i like lambda3");}}like = new Like3();like.lambda();// 5.匿名内部类:没有类的名称,必须借助接口或者父类like = new ILike() {@Overridepublic void lambda() {System.out.println("i like lambda4");}};like.lambda();// 6.用 lambda 简化:接口和方法都不要了,只留(参数列表) -> {函数体}like = () -> {System.out.println("i like lambda5");};like.lambda();}
}// 1.定义一个函数式接口:一种特殊接口,只包含一个抽象方法
interface ILike{void lambda();
}// 2.类实现接口
class Like implements ILike{@Overridepublic void lambda() {System.out.println("i like lambda");}
}

线程状态


线程同步(重点)


线程通信问题


高级主题


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

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

相关文章

ShellCheck工具:帮你发现Shell脚本安全隐患

作为一名运维工程师,你是否经常为Shell脚本中的各种隐藏bug而头疼?今天我要向大家推荐一个堪称"神器"的工具 - ShellCheck。它就像一位经验丰富的Shell编程导师,能够精准地发现你的脚本中的各种潜在问题,并给出专业的改进建议。它有多强? 想象一下,你有一位经验超过…

1.C++基础入门

C++基础入门。C++基础入门 1 C++初识 1.1 第一个C++程序 编写一个C++程序总共分为4个步骤创建项目 创建文件 编写代码 运行程序1.1.1 创建项目 ​ Visual Studio是我们用来编写C++程序的主要工具,我们先将它打开1.1.2 创建文件 右键源文件,选择添加->新建项给C++文件起个…

【Java开发】实现 License 认证(只校验有效期)

一、License介绍 License也就是版权许可证书,一般用于收费软件给付费用户提供的访问许可证明 1.1 应用场景应用部署在客户的内网环境 这种情况开发者无法控制客户的网络环境,也不能保证应用所在服务器可以访问外网 因此通常的做法是使用服务器许可文件,在应用启动的时候加载…

Proj CJI Paper Reading: AdaPPA: Adaptive Position Pre-Fill Jailbreak Attack Approach Targeting LLMs

AbstractBackground: 目前的jailbreak mutator方式更集中在语义level,更容易被防御措施检查到 本文: AdaPPA (Adaptive Position Pre-Filled Jailbreak Attack) Task: adaptive position pre-fill jailbreak attack approach Method: 利用模型的instruction following能力,先…

【Java安全】浅谈内存马

一、内存马概述1.1 内存马产生的背景1.2 Java内存马的基本原理1.3 Java内存马的类型1.4 Java内存马的使用场景二、内存马注入实战演示2.1 JSP注入Filter内存马2.2 Fastjson反序列化注入内存马2.3 注入Agent内存马三、内存马的检测与防御3.1 内存马定位排查思路3.2 工具查杀3.3 …

Diary - 2025.01.15

pkuwc 烂完了,😭😭😭其实是 pkuwc2024 的东西。 Day 0 坐飞机坐飞机,嘟嘟嘟。 大飞机!!!!!!!! 我觉得最厉害的是这个飞机有 3D 地图啊,太帅了!!!但是比较悲伤的是我直到要到了才知道,前面都在看 B 站缓存的视频😭。 感觉,太美丽了杭州!!! 坐飞机的时…

floor_plan_meshproject增加角度正则损失

数据结构 data.x #(128, 16) 16 = triangles(9) + confidence(7) """ 每个元素 [x1, y1, z1, x2, y2, z2, x3, y3, z3, c_v1, c_v2, c_v3, c_e1, c_e2, c_e3, c_f] """ data.y #(128)encoded_x #(128, 576) encoded_x_conv # (2, 96, 576) …

2025.1.15 html基础

学习了html的基础知识,包括:n越大,字体越小换行标签表示一个完整的段落水平线标签链接: 内容 例如: <! --a页面-->这是A页面。<! --b页面-->这是B页面。在浏览器中点击“这是A页面”,会跳转到b页面。

位图有关的格式信息

GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bmp); 获取 HBITMAP 句柄包含的位图信息结构,不包含像素数据内容。 typedef struct tagBITMAP {   LONG bmType; // 位图类型,必须为 0   LONG bmWidth; // 位图宽度(以像素为单位) …

Centos7.9安装kerberos

Centos7.9安装kerberos@目录一、背景二、Kerberos安装部署2.1kerberos服务端必要软件安装2.2配置krb5.conf2.3配置kdc.conf2.4配置kadm5.acl2.5创建Kerberos数据库2.6启动Kerberos服务2.7创建Kerberos管理员principal2.8客户端安装kerberos2.9Kerberos功能验证本人其他相关文章…

并发编程 - 初识线程

线程是操作系统单独执行任务的最小单元,分前台和后台,有优先级,经历多个状态。C#可设置线程优先级和类型,控制线程状态的方法有Start、Sleep等,但Suspend和Abort已被弃用。多线程编程需通过同步机制控制线程执行。01、什么是线程? 要深刻理解什么是线程,就需要了解计算机…