什么是线程?
- 线程(Thread)是程序执行流的最小单位。
- 在 Java 中,一个线程代表一个独立的执行路径,它运行在进程(Process)内部。
- 进程 vs 线程:
- 进程是操作系统分配资源的基本单位(如一个 Java 应用就是一个进程)。
- 线程是 CPU 调度的基本单位,多个线程共享同一个进程的内存空间(堆、方法区),但各自拥有独立的栈和程序计数器。
多线程程序在更低一层扩展了多任务的概念:单个程序看起来在同时完成多个任务。每个任务在一个线程(thread)中执行,线程是控制线程的简称。如果一个程序可以同时运行多个线程,则称这个程序是多线程的(multithreaded)。
在Java中创建线程
线程的创建有三种方式:
2.1 继承Thread类,重写run方法
- 创建一个继承Thread的类,重写run方法
- 在main方法中创建该类的对象,对象使用start方法启动线程,并开始执行该线程的任务
- 如果直接调用run方法,只会让当前线程执行任务
public class ThreadTest {
private static final int STEP = 100;
public static void main(String[] args) {
MyJob job = new MyJob();
job.start();
for (int i = 0; i < STEP; i++) {
System.out.println("main run: " + i);
}
}
}
class MyJob extends Thread {
private static final int STEP = 100;
@Override
public void run() {
for (int i = 0; i < STEP; i++) {
System.out.println("job run: " + i);
}
}
}
2.2 实现Runnable接口,重写run方法
- 创建一个类实现Runnable接口,重写run方法
- 在main方法中创建该类的对象
- 创建Thread类的对象,构造方法中传入Runnable接口的实现类对象
- Thread类的对象使用start方法启动线程,并开始执行该线程的任务
- 同样,如果直接调用run方法,只会让当前线程执行任务
public class RunnableTest {
private static final int STEP = 100;
public static void main(String[] args) {
MyAnotherJob job = new MyAnotherJob();
Thread t = new Thread(job);
t.start();
for (int i = 0; i < STEP; i++) {
System.out.println("main run: " + i);
}
}
}
class MyAnotherJob implements Runnable {
private static final int STEP = 100;
@Override
public void run() {
for (int i = 0; i < STEP; i++) {
System.out.println("job run: " + i);
}
}
}
2.3 实现Callable接口,重写call方法,配合FutureTask使用
Callable一般用于有返回结果的非阻塞的执行方法
同步非阻塞
- 创建一个类实现
Callable<V>接口
- 泛型 V 表示 call() 方法的返回类型(如 String、Integer 等)。
- 重写 call() 方法,在其中编写需要在线程中执行的逻辑,并返回结果。
- 创建 Callable 实例
- 即你自定义的实现了 Callable 的类的对象。
- 将 Callable 实例包装进 FutureTask
- FutureTask 是一个包装器,它实现了 Runnable 和 Future 接口,既能被线程执行,又能获取结果。
- 创建 Thread 对象,传入 FutureTask
- 因为 FutureTask 实现了 Runnable,所以可以作为 Thread 的构造参数。
- 启动线程(调用 start())
- 调用 FutureTask.get() 获取线程执行结果
- get() 方法会阻塞当前线程,直到任务完成并返回结果。
- 如果任务抛出异常,get() 会抛出 ExecutionException。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyTask implements Callable<String> {
private final long duration; // 模拟耗时操作的时间(毫秒)
public MyTask(long duration) {
this.duration = duration;
}
@Override
public String call() throws Exception {
System.out.println("任务开始执行...");
Thread.sleep(duration); // 模拟耗时操作
return "任务完成,耗时 " + duration + " 毫秒";
}
}
public class CallableExample {
public static void main(String[] args) {
Callable<String> callable = new MyTask(2000);
FutureTask<String> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask, "MyThread");
thread.start();
System.out.println("主线程继续执行其他任务...");
try {
String result = futureTask.get();
System.out.println("线程返回结果: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
多线程三种实现方式的对比
| 优点 | 缺点 | |
|---|---|---|
| 继承Thread类 | 实现简单,可以直接使用Thread类中的方法 | 不能再继承其他类,扩展性较差 |
| 实现Runnable接口 | 扩展性强,实现接口的同时可以继承其他类 | 编程比较复杂,不能使用Thread类的其他方法 |
| 实现Callable接口 | 扩展性强,实现接口的同时可以继承其他类 | 编程比较复杂,不能使用Thread类的其他方法 |
文章摘自:https://www.cnblogs.com/vitastic/p/19820800
