目录
前言
1.线程是什么
一个线程就是一个执行流(指的就是程序中的指令是按照什么顺序被执行的。你可以把它想象成做事情的流程图)。每个线程之间都可以按照顺序执行自己的代码,多个线程之间“同时”执行着多份代码。
举个日常生活中的例子:
想象你家里人(妈妈)做晚饭。比如:
- 洗菜
- 切菜
- 炒菜
- 煮饭
- 摆盘
只能一件事情做完,再做下一件。执行流是线性的。就像单线程程序。
于是妈妈请了家里人(你和你弟弟)一起来帮忙:
- 妈妈在切菜(线程A)
- 你在洗菜(线程B)
- 弟弟在煮饭 (线程C)
大家同时进行,每个人都有自己的执行流,但是大家都最终完成了一个目标:做晚饭。这就像程序中不同线程协同完成任务。
于是我们把上述情况称为多线程,将一个大任务分解成不同的小任务,交给不同的执行流来执行。其中,你和你弟弟都是被叫来的,所以妈妈一般被称为主线程,主线程最开始独立负责所有工作。
2.为什么要有线程?
- 首先,“并发编程”是“刚需”
- 单核CPU的性能已经到了瓶颈,要想提高算力,就需要多核CPU,而并发编程能够更充分利用多核CPU资源。
- 有些任务厂家需要等待IO,为了让这个等待时间能够去做一些其他的工作,也需要用到并发编程。
- 其次,线程就是比进程更轻量
- 创建线程比创建进程更快
- 销毁线程比销毁进程更快
- 调度线程比调度进程更快
虽然线程比进程更轻量,但是线程创建多了也需要考虑到性能,于是又有了“线程池(ThreadPool)”和“协程(Coroutine)”。
3.进程和线程的区别
- 进程是包含线程的,每个进程至少有一个线程存在(即主线程)
- 进程和进程之间不共享内存空间,同一个进程的线程之间共享同一份内存空间
比如之前讲到的例子:
概念 做饭比喻 进程 整个厨房,有独立空间 主线程 妈妈,第一个且必须存在的(不然大家都别吃晚饭了) 多线程 妈妈+你+弟弟同时在厨房工作 线程共享 所有人都能直接使用厨房里的食材、厨具(共享内存) 进程隔离 邻居家的厨房是完全独立的,不能直接拿对方的食材或厨具(进程间内存隔离) 进程间通信 如果真要借食材或厨具,需要走到对方家门口敲门请求(IPC) 整个厨房就像一个进程,厨房有自己独立的空间(内存空间),和其他厨房(其他进程)是隔离的,比如邻居家的厨房和你家的厨房不共享食材和厨具
- 进程是系统分配资源的最小单位
- 线程是系统调度的最小单位
4.操作系统也有线程,和java的线程有什么关系呢?
java标准库中的线程(Thread)可以视为是对操作系统提供的API进行了进一步抽象和封装.
引言
编写第一个多线程程序
实例代码
class MyThread extends Thread { @Override public void run() { while (true) { System.out.println("hello thread"); } } } public class ThreadDemo { public static void main(String[] args) { MyThread thread=new MyThread(); thread.start(); while (true) { System.out.println("hello main"); } } }
使用.start()创建并启动线程,而不是直接调用run()
输出结果
![]()
代码分析
为什么我上述结果看起来像顺序执行而不是交错执行呢?
可能的原因有几个,常见的是:
主线程执行得太快了 —— 新开出来的线程还没来得及抢到 CPU 资源,主线程就已经输出完了。
子线程启动有延迟 —— 调用
start()
后,线程不是立刻运行,而是由操作系统调度,调度本身需要一点时间。缺少等待子线程结束的操作 —— 如果主线程执行完直接退出,子线程就可能被强制终止,或者被 "孤立"。
通常,为了在学习多线程时观察更明显的"并发交错",可以做两件事:
在循环里加一点点延迟,比如
Thread.sleep(10);
在主线程里等待子线程结束,比如用
join()
方法。为什么看上去每次都是"hello main"先执行?
线程的创建也是有成本的,只不过和进程相比开销比较小,但不是没有
改进
上述代码我们希望它执行得慢一点,因此可以使用线程中提供的sleep方法
由于可能会被提前唤醒(报错),因此我们加入try-catch
改进后的run方法
可以试着自己写一遍,然后查看运行结果
java中创建线程的写法其实有很多种
一.继承Thread重写run
1.继承Thread
2.重写run
二.实现Runable接口
1.继承Runable接口
2.重写run方法
三.使用匿名内部类,继承Thread
四.使用匿名内部类,实现Runable
五.使用Lambda表达式(最推荐)
其中Lambda就是一个匿名函数