文章目录
并发编程: 通过写特殊的代码,把多个CPU核心都利用起来,这样的代码就称为“并发编程”。
多进程编程,就是一种典型的并发编程。
多进程编程,最大的问题是进程太“重”(创建进程 / 销毁进程的开销比较大)
为了解决进程开销比较大的问题,于是发明了“线程”(Thread)
线程可以理解成更轻量的进程,它也能解决并发编程的问题,但是创建/销毁的开销,比进程更低
所谓的进程,在系统中是通过PCB这样的结构体来描述,通过链表的形式来组织的.
对于系统中,线程,同样也是通过PCB来描述的(Linux)
一个进程,其实是一组PCB.
一个线程,是一个PCB.
一个进程中,可以包含多个线程,此时每个线程,都可以独立的到CPU上调度执行
线程是系统"调度执行"的基本单位
进程是系统"资源分配"的基本单位
如果有多个线程在一个进程中,那么每个线程都会有自己的状态,优先级,上下文,记账信息, 每个线程都会各自独立的在CPU上调度执行.
同一个进程中的这些线程,共用一份系统资源(CPU,内存,硬盘,网络带宽…)
线程比进程更轻量,主要就在于创建线程省去了"分配资源"(开销大)的过程,销毁线程也省去了"释放资源"的过程.
一旦创建进程,同时也会创建第一个线程 => 就会负责分配资源…
一旦后续创建第二,第三个线程 就不必再重新分配资源了
举个例子:
进程线程的概念和区别总结
- 进程包含线程
一个进程里可以有一个线程,也可以有多个线程,不能没有线程 - 进程是系统资源分配的基本单位
线程是系统调度执行的基本单位 - 同一个进程里的线程之间,共用同一份系统资源(内存,硬盘,网络带宽等…)尤其是"内存资源",就是代码中定义的变量/对象…(编程中,多个线程是可以共用同一份变量的)
- 线程是当下实现并发编程的主流方式,通过多线程就可以充分利用好多核CPU
但是,也不是线程数目越多就越好的,线程数目达到一定程度,把多个核心都利用充分了之后,此时继续增加线程,无法再提供效率,甚至可能会影响效率(线程调度也是有开销的) - 多个线程之间,可能会相互影响.线程安全问题,一个线程抛出异常,可能会把其他线程一起带走.
- 多个进程之间,一般不会相互影响,一个进程崩溃了,不会影响到其他进程(这一点也称为"进程的隔离性").
如何创建线程
1.继承Thread重写run
创建类,继承Thread重写run方法
class MyThread extends Thread {
@Override
public void run() {
//这里写的代码,就是即将创建出的线程要执行的逻辑
}
}
在main方法中创建实例
public static void main() {
MyThread t = new MyThread();
//创建线程
t.start();
}
调用start就会在进程内部创建出一个新的线程,新的线程就会执行刚才run里面的代码
2.实现Runnable重写run
class MyRunnable implements Runnable {
@Override
public void run() {
//描述了线程要完成的逻辑是啥
}
}
//通过Runnable的方法来创建线程
public class Demo2 {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread t = new Thread(runnable);
t.start();
}
}
Runnable 就是用来描述"要执行的任务"是什么.
有的人认为Runnable这种做法更有利于"解耦合"
理由:这个Runnable只是一个任务,并不是和"线程"这样的概念强相关,后续执行这个任务的载体,既可以是线程,也可以是其他的东西(比如线程池,虚拟线程).
3.继承Thread重写run,通过匿名内部类来实现
本质上和1一样
public static void main(String[] args) {
Thread t = new Thread() {
@Override
public void run() {
//描述了线程要完成的逻辑是啥
}
};
t.start();
}
- 定义内部类,这个类是Thread的子类
- 类的内部重写父类的run方法
- 创建一个子类的实例,并且把实例的引用赋值给了t
4. 实现Runnable重写run,通过匿名内部类来实现
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
//描述了线程要完成的逻辑是啥
}
});
//创建线程
t.start();
}
5.基于lambda表达式来创建
public static void main(String[] args) {
Thread t = new Thread(()->{
//描述了线程要完成的逻辑是啥
});
t.start();
}
虚拟线程
线程是轻量级进程,因为进程太重量了
随着对于性能要求进一步提高,我们开始嫌弃线程也太重量了
于是引入了"协程"(轻量级线程,纤程)
这个协程概念,在后续的Java版本中被引入了,起的名字叫"虚拟线程"