进程、线程和协程
进程、线程和协程
进程
进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建、运行到消亡的过程。
在Java中,当我们启动main函数其实就是启动了一个JVM进程,而main函数所在的线程其实就是这个进程中的一个线程,也称主线程。
在 Windows 中通过查看任务管理器的方式,我们就可以清楚看到 Windows 当前运行的进程(.exe 文件的运行)。
线程
线程与进程相似,但线程是一个比进程更小的执行单位,一个进程在其执行过程中可以产生多个线程。
各进程之间基本上是相互独立的,与进程不同的是同类线程共享进程的堆和方法区资源,但每个线程都有自己的程序计数器、Java虚拟机栈和本地方法栈。
系统产生一个线程或是在多个线程之间切换工作时,负担要比进程小得多,因此线程也被称作轻量级进程。
Java 程序天生就是多线程程序,一个 Java 程序的运行是 main 线程和多个其他线程同时运行。
进程和线程的区别
根本区别: 进程时操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。
资源开销: 每个进程都有自己独立的代码和数据空间(程序上下文),进程之间的切换开销比较大;线程可以看作轻量级进程,同类的线程共享进程的堆和方法区(JDK1.7及之前实现为永久代,JDK1.8及之后实现为元空间)资源,但是每个线程都有自己的程序计数器、Java虚拟机栈和本地方法栈,线程之间的切换开销比较小。
包含关系: 如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
内存分配: 同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的。
影响关系: 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉,所以多进程要比多线程健壮。
执行过程: 每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行。
总结: 线程是进程划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护;而进程正相反。
协程
协程是一种轻量级的线程,协程的实现方式是在一个线程中,通过切换执行上下文来实现多个任务的并发执行。协程的切换不需要操作系统的介入,因此协程的切换速度非常快,开销也非常小。协程的特点是可以在一个线程中开启多个协程,每个协程之间共享线程的内存空间,因此协程也可以完成复杂的任务,如异步编程、爬虫、游戏等。
线程与协程的区别
线程和协程都是多任务处理的技术,但是它们之间有很多区别,下面列举几点:
-
调度方式
线程是由操作系统负责调度的,它们是操作系统的一种资源。线程的调度和切换需要操作系统的介入,开销比较大,但是线程的并发性和并行性比较高。
**协程是由程序控制的,它们是用户空间的资源,不依赖于操作系统。**协程的切换和调度由程序控制,因此开销比较小,但是并发性和并行性比较低。 -
内存占用
每个线程都需要独立的堆栈和寄存器等资源,协程则共享线程的这些资源,内存占用比较小。 -
编程复杂度
线程需要考虑锁、同步、异步等问题,编程复杂度比较高。
协程可以通过 yield、await 等关键字来实现状态的保存和恢复,编程复杂度比较低。 -
执行效率
线程切换和调度需要操作系统的介入,开销比较大,执行效率比较低。
协程切换和调度由程序控制,开销比较小,执行效率比较高。
总的来说,线程适用于 CPU 密集型的任务,协程适用于 I/O 密集型的任务。
线程适用于 CPU 密集型的任务,协程适用于 I/O 密集型的任务
线程和协程的适用场景主要是由于它们在任务调度方面的特点决定的。
对于 CPU 密集型的任务,比如大量的计算、排序等,需要大量的 CPU 资源,线程可以让程序在多个 CPU 核心上并行执行,从而提高执行效率。但是线程的切换和调度需要操作系统的介入,开销比较大,因此线程数量过多时,会降低程序的效率。
对于 I/O 密集型的任务,比如网络请求、文件读写等,需要等待 I/O 操作完成后才能继续执行后续任务,这时线程的执行效率就会受到限制。而协程可以通过 yield、await 等关键字来实现状态的保存和恢复,从而避免了线程切换的开销,提高了程序的效率,因此协程适用于 I/O 密集型的任务。
总之,线程适用于需要大量的 CPU 资源的任务,而协程适用于需要大量的 I/O 操作的任务。在实际应用中,我们需要根据具体的场景选择合适的技术来实现任务的处理。
创建线程的三种方式
使用继承Thread类的方式创建多线程
Thread类是Java提供的线程顶级类,继承Thread类可快速定义线程。
- 使用多线程实现龟兔赛跑
public class TortoiseThread extends Thread{
@Override
public void run() {
while (true) {
System.out.println("乌龟领先了,加油.....," +
"当前线程的名称:" + this.getName() +
",当前线程的优先级别:" + this.getPriority());
}
}
public static void main(String[] args) {
TortoiseThread tortoiseThread = new TortoiseThread();
tortoiseThread.setName("乌龟线程");
tortoiseThread.start();
Thread.currentThread().setName("兔子线程");
while(true){
System.out.println("兔子领先了,add oil....,当前线程名称:"
+Thread.currentThread().getName()+",当前线程的优先级别:"
+Thread.currentThread().getPriority());
}
}
}
- 运行结果截取(这里只截取了部分结果,应该是无限循环的)
乌龟领先了,加油.....,当前线程的名称:乌龟线程,当前线程的优先级别:5
乌龟领先了,加油.....,当前线程的名称:乌龟线程,当前线程的优先级别:5
兔子领先了,add oil....,当前线程名称:兔子线程,当前线程的优先级别:5
兔子领先了,add oil....,当前线程名称:兔子线程,当前线程的优先级别:5
使用实现Runnable接口的方式创建多线程
- 使用多线程实现龟兔赛跑
public class TortoiseRunnable implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("乌龟领先了,加油.....," +
"当前线程的名称:" + Thread.currentThread().getName() +
",当前线程的优先级别:" + Thread.currentThread().getPriority());
}
}
public static void main(String[] args) {
Thread thread = new Thread(new TortoiseRunnable());
thread.setName("乌龟线程");
thread.start();
Thread.currentThread().setName("兔子线程");
while(true){
System.out.println("兔子领先了,add oil....,当前线程名称:"
+Thread.currentThread().getName()+",当前线程的优先级别:"
+Thread.currentThread().getPriority());
}
}
}
- 运行结果截取(这里只截取了部分结果,应该是无限循环的)
乌龟领先了,加油.....,当前线程的名称:乌龟线程,当前线程的优先级别:5
乌龟领先了,加油.....,当前线程的名称:乌龟线程,当前线程的优先级别:5
兔子领先了,add oil....,当前线程名称:兔子线程,当前线程的优先级别:5
兔子领先了,add oil....,当前线程名称:兔子线程,当前线程的优先级别:5
使用实现Callable接口的方式创建多线程
- 使用多线程实现龟兔赛跑
public class TortoiseCallable implements Callable {
@Override
public Object call() throws Exception {
while (true) {
System.out.println("乌龟领先了,加油.....," +
"当前线程的名称:" + Thread.currentThread().getName() +
",当前线程的优先级别:" + Thread.currentThread().getPriority());
}
}
public static void main(String[] args) {
TortoiseCallable tortoiseCallable = new TortoiseCallable();
FutureTask futureTask = new FutureTask(tortoiseCallable);
Thread thread = new Thread(futureTask);
thread.setName("乌龟线程");
thread.start();
Thread.currentThread().setName("兔子线程");
while(true){
System.out.println("兔子领先了,add oil....,当前线程名称:"
+Thread.currentThread().getName()+",当前线程的优先级别:"
+Thread.currentThread().getPriority());
}
}
}
- 运行结果截取(这里只截取了部分结果,应该是无限循环的)
乌龟领先了,加油.....,当前线程的名称:乌龟线程,当前线程的优先级别:5
乌龟领先了,加油.....,当前线程的名称:乌龟线程,当前线程的优先级别:5
兔子领先了,add oil....,当前线程名称:兔子线程,当前线程的优先级别:5
兔子领先了,add oil....,当前线程名称:兔子线程,当前线程的优先级别:5