进程、线程、管程、协程
进程 (Process)
进程是操作系统分配资源的基本单位。每个进程都有自己的内存空间、文件描述符、堆栈等资源。
进程的特点
独立性:进程之间是独立的,互不干扰。一个进程的崩溃不会影响其他进程。
资源丰富:每个进程拥有独立的资源,包括内存、文件句柄等。
开销大:创建和销毁进程的开销较大,进程间通信(IPC)也相对复杂。
上下文切换:进程的上下文切换开销较大,因为需要切换内存空间和资源。
使用场景
适用于需要强隔离和独立资源的场景,如独立的服务、应用程序等。
线程 (Thread)
线程是进程内的执行单元,一个进程可以包含多个线程。线程共享进程的资源(如内存空间、文件描述符)。
线程的特点
共享资源:同一进程内的线程共享内存和资源,通信方便。
轻量级:线程的创建和销毁开销较小,上下文切换较快。
并发执行:多线程可以并发执行,提高程序的响应速度和资源利用率。
同步问题:由于共享资源,线程间需要同步机制(如锁)来避免资源竞争和数据不一致。
使用场景
适用于需要并发执行的任务,如多任务处理、并行计算等。
管程 (Monitor)
管程是一种高级的同步机制,用于管理共享资源的并发访问。它将共享资源和访问资源的代码封装在一起,通过条件变量和互斥锁来实现同步。
管程特点
封装性:将共享资源和同步代码封装在一起,提供更高层次的抽象。
互斥访问:通过互斥锁确保同一时刻只有一个线程可以访问共享资源。
条件同步:使用条件变量来协调线程间的执行顺序。
使用场景
适用于需要对共享资源进行复杂同步操作的场景,如操作系统内核、并发数据结构等。
协程 (Coroutine)
协程是一种比线程更轻量级的并发执行单元。协程由程序自身调度,而不是由操作系统内核调度。协程可以在执行过程中主动让出控制权,以便其他协程运行。
协程特点
- 轻量级:协程的创建和切换开销极小,通常在用户态完成。
- 主动让出:协程通过显式的调用(如yield)让出控制权,实现合作式多任务。
- 非抢占式:协程之间的切换是合作式的,不存在抢占问题。
- 栈独立:每个协程有自己的栈,避免了线程间共享栈带来的同步问题。
使用场景
适用于需要大量并发任务且切换频繁的场景,如高并发网络服务器、异步编程等。
用户线程与守护线程区别
用户线程
用户线程是应用程序创建的普通线程,也称为非守护线程。当所有用户线程都结束时,Java 虚拟机 (JVM) 也会退出。
特点
生命周期:用户线程的生命周期由应用程序控制。只要有一个用户线程在运行,JVM 就会继续运行。
重要性:用户线程通常用于执行应用程序的主要任务,例如处理业务逻辑、执行计算等。
关闭 JVM:JVM 只有在所有用户线程都结束后才会退出,即使还有守护线程在运行。
使用场景
适用于需要执行重要任务且不能中途被终止的线程。例如:处理用户请求的线程,执行关键业务逻辑的线程
守护线程 (Daemon Thread)
守护线程是为其他线程提供服务和支持的线程。当所有非守护线程(用户线程)都结束时,JVM 会自动退出,即使守护线程还在运行。
特点
生命周期:守护线程的生命周期依赖于用户线程。当所有用户线程结束时,守护线程也会自动终止。
后台任务:守护线程通常用于执行后台任务,如垃圾回收、日志记录等。
低优先级:守护线程通常优先级较低,因为它们主要为用户线程提供支持。
使用场景
适用于执行后台任务或辅助任务的线程,这些任务不需要在 JVM 退出时完成。例如:JVM 的垃圾回收线程,日志记录线程,监控和统计线程
Java线程的创建方式
继承Thread类
通过继承java.lang.Thread类并重写其run方法来创建线程。
实现Runnable接口
通过实现java.lang.Runnable接口并将其传递给Thread对象来创建线程。
实现Callable接口和使用FutureTask
通过实现java.util.concurrent.Callable接口来创建线程,并使用FutureTask来管理返回结果。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Callable result";
}
public static void main(String[] args) {
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<>(myCallable);
Thread thread = new Thread(futureTask);
thread.start();
try {
System.out.println("Result: " + futureTask.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
使用线程池
通过java.util.concurrent.ExecutorService创建和管理线程池,避免手动创建和管理线程。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
executorService.execute(() -> {
System.out.println("Thread pool task is running");
});
}
executorService.shutdown();
}
}
使用Lambda表达式 (Java 8及以上)
通过Lambda表达式简化Runnable接口的实现。
public class LambdaExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> System.out.println("Lambda thread is running"));
thread.start();
}
}