Java提供了多种实现多线程的方式,每种方式各有特点,适用于不同的场景。下面将全面介绍Java中实现多线程的几种主要方法。
1. 继承Thread类
特点:
- 最简单直接的方式
- 通过继承Thread类并重写run()方法
- 单继承限制(Java不允许多继承)
实现步骤:
- 创建Thread的子类
- 重写run()方法
- 创建子类实例
- 调用start()方法启动线程
示例代码:
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行: " + i);
try {
sleep((int)(Math.random() * 1000)); // 随机休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread t1 = new MyThread("线程A");
MyThread t2 = new MyThread("线程B");
t1.start(); // 启动线程
t2.start();
}
}
注意事项:
- 直接调用run()方法不会启动新线程,只是在当前线程中执行
- 必须调用start()方法才能启动新线程
- 每个线程只能start()一次
2. 实现Runnable接口
特点:
- 更灵活,避免了单继承的限制
- 适合多个线程处理同一资源的情况
- 可以方便地使用线程池
实现步骤:
- 创建实现Runnable接口的类
- 实现run()方法
- 创建Thread对象并传入Runnable实例
- 调用Thread对象的start()方法
示例代码:
class MyRunnable implements Runnable {
private String name;
public MyRunnable(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行: " + i);
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
MyRunnable r1 = new MyRunnable("线程A");
MyRunnable r2 = new MyRunnable("线程B");
new Thread(r1).start();
new Thread(r2).start();
// Java 8 Lambda表达式简化写法
new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Lambda线程运行: " + i);
}
}).start();
}
}
3. 实现Callable接口
特点:
- 可以返回执行结果
- 可以抛出异常
- 通常与FutureTask或ExecutorService配合使用
实现步骤:
- 创建实现Callable接口的类
- 实现call()方法
- 创建FutureTask实例包装Callable对象
- 创建Thread对象并传入FutureTask实例
- 调用start()方法启动线程
- 通过FutureTask获取结果
示例代码:
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<Integer> {
private String name;
public MyCallable(String name) {
this.name = name;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行: " + i);
sum += i;
Thread.sleep((int)(Math.random() * 1000));
}
return sum;
}
}
public class CallableDemo {
public static void main(String[] args) throws Exception {
MyCallable c1 = new MyCallable("线程A");
MyCallable c2 = new MyCallable("线程B");
FutureTask<Integer> ft1 = new FutureTask<>(c1);
FutureTask<Integer> ft2 = new FutureTask<>(c2);
new Thread(ft1).start();
new Thread(ft2).start();
System.out.println("线程A结果: " + ft1.get()); // 阻塞直到获取结果
System.out.println("线程B结果: " + ft2.get());
}
}
4. 使用线程池
特点:
- 减少线程创建和销毁的开销
- 提高系统资源利用率
- 提供更强大的线程管理功能
常用线程池:
- FixedThreadPool:固定大小线程池
- CachedThreadPool:可缓存线程池
- ScheduledThreadPool:定时任务线程池
- SingleThreadExecutor:单线程线程池
实现步骤:
- 创建线程池
- 提交任务(Runnable或Callable)
- 关闭线程池
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 创建固定大小的线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
// 提交任务
for (int i = 0; i < 10; i++) {
final int taskId = i;
pool.execute(() -> {
System.out.println("任务" + taskId + "由" + Thread.currentThread().getName() + "执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 关闭线程池
pool.shutdown();
try {
// 等待所有任务完成
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow();
}
} catch (InterruptedException e) {
pool.shutdownNow();
}
}
}
5. Fork/Join框架(Java 7+)
特点:
- 适合分而治之的任务
- 工作窃取算法提高效率
- 自动利用多核处理器
核心类:
- ForkJoinPool:特殊线程池
- RecursiveTask:有返回值的任务
- RecursiveAction:无返回值的任务
示例代码:
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;
class SumTask extends RecursiveTask<Long> {
private static final int THRESHOLD = 1000; // 阈值
private long[] array;
private int start;
private int end;
public SumTask(long[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
// 如果任务足够小,直接计算
if (end - start <= THRESHOLD) {
long sum = 0;
for (int i = start; i < end; i++) {
sum += array[i];
}
return sum;
}
// 任务太大,拆分为两个子任务
int middle = (start + end) / 2;
SumTask leftTask = new SumTask(array, start, middle);
SumTask rightTask = new SumTask(array, middle, end);
// 并行执行子任务
leftTask.fork();
rightTask.fork();
// 合并结果
return leftTask.join() + rightTask.join();
}
}
public class ForkJoinDemo {
public static void main(String[] args) {
// 创建测试数组
long[] array = new long[10000];
for (int i = 0; i < array.length; i++) {
array[i] = i + 1;
}
// 创建ForkJoinPool
ForkJoinPool pool = new ForkJoinPool();
SumTask task = new SumTask(array, 0, array.length);
// 提交任务并获取结果
long result = pool.invoke(task);
System.out.println("计算结果: " + result);
}
}
6. 线程同步与通信
6.1 同步方法
public synchronized void add(int value) {
this.count += value;
}
6.2 同步代码块
public void add(int value) {
synchronized(this) {
this.count += value;
}
}
6.3 使用Lock对象
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count = 0;
private Lock lock = new ReentrantLock();
public void add(int value) {
lock.lock();
try {
this.count += value;
} finally {
lock.unlock();
}
}
}
6.4 线程通信(wait/notify)
class Message {
private String message;
private boolean empty = true;
public synchronized String read() {
while (empty) {
try {
wait();
} catch (InterruptedException e) {}
}
empty = true;
notifyAll();
return message;
}
public synchronized void write(String message) {
while (!empty) {
try {
wait();
} catch (InterruptedException e) {}
}
empty = false;
this.message = message;
notifyAll();
}
}
7. 选择多线程实现方式的建议
- 简单任务:实现Runnable接口或使用Lambda表达式
- 需要返回值:使用Callable和Future
- 大量短期任务:使用线程池(ExecutorService)
- 分治任务:使用Fork/Join框架
- 定时任务:使用ScheduledExecutorService
- 避免直接继承Thread:除非需要重写Thread的方法
8. 多线程编程注意事项
- 线程安全:注意共享资源的同步访问
- 死锁避免:避免多个线程互相等待对方释放锁
- 性能考量:线程创建和上下文切换有开销
- 资源释放:确保线程结束时释放所有资源
- 异常处理:线程中的异常通常不会传播到主线程
掌握这些多线程实现方式后,你可以根据具体需求选择最适合的方法来构建高效、可靠的并发Java应用程序。