一、线程的基础概念
1.1 进程与线程
进程
进程是操作系统分配资源的基本单位。每个运行中的程序都可以视为一个进程,操作系统会为它分配内存和其他资源。例如,我们启动钉钉、浏览器等程序时,它们就是在操作系统中以进程的形式存在。
线程
线程是进程中的执行单位,是CPU调度的基本单位。一个进程至少有一个线程,而这个线程负责执行进程的具体任务。线程是CPU调度的核心,每个线程都会执行进程中的某一部分代码。
举个例子:
- 想象一座100平米的房子,这个房子就是进程。
- 房子里的每个人就是一个线程,房子里的人可以同时做不同的事情,比如吃饭、看书、休息等。
进程与线程的区别
| 特性 | 进程 | 线程 |
|---|---|---|
| 资源分配 | 操作系统为进程分配资源 | 线程共享进程的资源 |
| 内存空间 | 独立的内存空间 | 共享进程的内存空间 |
| 调度 | 操作系统调度进程 | CPU调度线程 |
| 创建开销 | 创建和销毁的开销较大 | 线程创建和销毁的开销较小 |
| 通信 | 进程间通信复杂,需要借助IPC机制 | 线程间通信简单,通过共享内存等方式 |
1.2 多线程
什么是多线程?
多线程是指在同一个进程中同时运行多个线程。通过多线程,可以让程序在进行I/O操作时不浪费CPU时间,提升系统性能。例如,Tomcat服务器可以同时处理多个客户端请求,而不是顺序排队。
多线程的优点
- 提高CPU利用率:避免了CPU空闲,提升了系统效率。
- 提升用户体验:在进行IO操作时,可以让其他任务继续执行,不会阻塞主流程。
多线程的局限
- 线程切换开销:线程数量过多时,CPU切换线程的开销变大,导致性能下降。
- 任务拆分困难:某些任务由于其复杂性,不容易拆分成多个子任务进行并行处理。
- 线程安全问题:多个线程操作同一资源时,可能会引发数据不一致的安全问题,甚至造成死锁。
1.3 串行、并行与并发
- 串行:任务按顺序执行,一个任务完成后才能执行下一个任务。
- 并行:多个任务同时执行,通常在多核CPU中可以实现。
- 并发:多个任务看似同时执行,但实际上是CPU在短时间内快速切换任务,单核CPU通过并发模拟并行。
1.4 同步与异步、阻塞与非阻塞
同步与异步
- 同步:调用者需要等待操作完成后才会得到结果。
- 异步:调用者发出请求后,不需要等待结果,可以继续执行其他操作。
阻塞与非阻塞
- 阻塞:调用者发出请求后,需要一直等待结果,不做其他事情。
- 非阻塞:调用者发出请求后,不需要等待结果,可以继续执行其他任务。
最佳方案 :在实际开发中,异步非阻塞是提升效率的最佳方式,尤其是在处理并发任务时。
二、线程的创建与使用
2.1 线程的创建
继承Thread类
通过继承Thread类并重写run方法来创建线程。调用start()方法启动线程。
java
public class MyTest {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running...");
}
}
实现Runnable接口
实现Runnable接口并重写run方法,可以避免Java中类的单继承限制。通过创建Thread对象来启动线程。
java
public class MyTest {
public static void main(String[] args) {
Runnable task = () -> System.out.println("Runnable is running...");
Thread t1 = new Thread(task);
t1.start();
}
}
使用Callable与Future
Callable接口允许线程执行任务并返回结果,结合Future可以获取线程执行结果。
java
public class MyTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> task = () -> 10 + 20;
FutureTask<Integer> future = new FutureTask<>(task);
Thread t1 = new Thread(future);
t1.start();
System.out.println("Result: " + future.get()); // 等待线程执行结果
}
}
基于线程池创建线程
线程池是Java中管理线程的一种方式,可以通过ExecutorService创建线程池,避免频繁创建销毁线程的开销。
java
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> System.out.println("Thread from pool is running..."));
executor.shutdown();
2.2 线程的状态
线程的状态有以下几种:
- NEW :线程被创建,但尚未调用
start()方法。 - RUNNABLE:线程处于可运行状态,等待CPU调度。
- BLOCKED:线程因竞争资源(如锁)被阻塞。
- WAITING :线程进入等待状态,需要外部操作(如
notify)唤醒。 - TIMED_WAITING:线程在限定时间内等待。
- TERMINATED:线程执行完毕,生命周期结束。
通过以下代码示例查看线程状态:
java
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("State before start: " + t1.getState());
t1.start();
Thread.sleep(500);
System.out.println("State after start: " + t1.getState());
}
2.3 线程的常用方法
获取当前线程
java
Thread current = Thread.currentThread();
System.out.println("Current thread: " + current.getName());
设置线程的优先级
java
Thread t1 = new Thread(() -> System.out.println("Task 1"));
t1.setPriority(Thread.MAX_PRIORITY);
t1.start();
线程休眠
java
Thread.sleep(1000); // 让当前线程休眠1000毫秒
线程的join方法
join方法让当前线程等待,直到调用的线程执行完毕。
java
Thread t1 = new Thread(() -> {
System.out.println("Thread 1 is running...");
});
t1.start();
t1.join(); // 当前线程等待t1完成