Appearance
18.2 创建线程
创建线程的方法
在 Java 中,创建线程有以下几种方法:
方法一:继承 Thread 类
步骤:
- 继承
Thread类 - 重写
run()方法,在其中定义线程要执行的任务 - 创建线程对象并调用
start()方法启动线程
示例:
java
public class MyThread extends Thread {
@Override
public void run() {
// 线程执行的任务
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 执行: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadCreationExample {
public static void main(String[] args) {
// 创建线程对象
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
// 设置线程名称
thread1.setName("线程1");
thread2.setName("线程2");
// 启动线程
thread1.start();
thread2.start();
// 主线程继续执行
System.out.println(Thread.currentThread().getName() + " 执行完毕");
}
}方法二:实现 Runnable 接口
步骤:
- 实现
Runnable接口 - 实现
run()方法,在其中定义线程要执行的任务 - 创建
Runnable对象 - 将
Runnable对象作为参数传递给Thread构造函数 - 调用
start()方法启动线程
示例:
java
public class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行的任务
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 执行: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunnableCreationExample {
public static void main(String[] args) {
// 创建 Runnable 对象
MyRunnable runnable = new MyRunnable();
// 创建线程对象
Thread thread1 = new Thread(runnable, "线程1");
Thread thread2 = new Thread(runnable, "线程2");
// 启动线程
thread1.start();
thread2.start();
// 主线程继续执行
System.out.println(Thread.currentThread().getName() + " 执行完毕");
}
}方法三:使用 Lambda 表达式
步骤:
- 使用 Lambda 表达式实现
Runnable接口 - 将 Lambda 表达式作为参数传递给
Thread构造函数 - 调用
start()方法启动线程
示例:
java
public class LambdaThreadExample {
public static void main(String[] args) {
// 使用 Lambda 表达式创建线程
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 执行: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程1");
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 执行: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程2");
// 启动线程
thread1.start();
thread2.start();
// 主线程继续执行
System.out.println(Thread.currentThread().getName() + " 执行完毕");
}
}方法四:使用 Callable 和 Future
步骤:
- 实现
Callable接口,泛型参数指定返回值类型 - 实现
call()方法,在其中定义线程要执行的任务并返回结果 - 创建
Callable对象 - 创建
ExecutorService对象 - 使用
submit()方法提交Callable对象,返回Future对象 - 使用
get()方法获取线程执行结果 - 关闭
ExecutorService
示例:
java
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableExample implements Callable<Integer> {
private int start;
private int end;
public CallableExample(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = start; i <= end; i++) {
sum += i;
}
System.out.println(Thread.currentThread().getName() + " 计算: " + start + " 到 " + end + " 的和为 " + sum);
return sum;
}
}
public class CallableThreadExample {
public static void main(String[] args) {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
// 创建 Callable 对象
Callable<Integer> task1 = new CallableExample(1, 100);
Callable<Integer> task2 = new CallableExample(101, 200);
// 提交任务
Future<Integer> future1 = executor.submit(task1);
Future<Integer> future2 = executor.submit(task2);
try {
// 获取结果
int result1 = future1.get();
int result2 = future2.get();
System.out.println("总结果: " + (result1 + result2));
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭线程池
executor.shutdown();
}
}
}继承 Thread vs 实现 Runnable
| 特性 | 继承 Thread | 实现 Runnable |
|---|---|---|
| 代码结构 | 简单直接 | 更加灵活 |
| 继承限制 | 只能继承一个类,不能再继承其他类 | 可以实现多个接口,还可以继承其他类 |
| 资源共享 | 每个线程有自己的实例,资源不共享 | 多个线程可以共享同一个 Runnable 实例 |
| 推荐使用 | 简单场景 | 复杂场景,推荐使用 |
示例:资源共享
实现 Runnable 接口实现资源共享
java
public class ResourceSharingExample implements Runnable {
private int count = 0;
@Override
public void run() {
for (int i = 0; i < 5; i++) {
count++;
System.out.println(Thread.currentThread().getName() + " 执行,count = " + count);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ResourceSharingDemo {
public static void main(String[] args) {
// 创建一个 Runnable 实例
ResourceSharingExample runnable = new ResourceSharingExample();
// 创建两个线程,共享同一个 Runnable 实例
Thread thread1 = new Thread(runnable, "线程1");
Thread thread2 = new Thread(runnable, "线程2");
// 启动线程
thread1.start();
thread2.start();
}
}继承 Thread 类实现资源共享
java
public class ThreadResourceSharing extends Thread {
private static int count = 0; // 使用静态变量实现共享
@Override
public void run() {
for (int i = 0; i < 5; i++) {
count++;
System.out.println(Thread.currentThread().getName() + " 执行,count = " + count);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadResourceSharingDemo {
public static void main(String[] args) {
// 创建两个线程
ThreadResourceSharing thread1 = new ThreadResourceSharing();
ThreadResourceSharing thread2 = new ThreadResourceSharing();
// 设置线程名称
thread1.setName("线程1");
thread2.setName("线程2");
// 启动线程
thread1.start();
thread2.start();
}
}线程池
线程池是一种管理线程的机制,它可以重用线程,减少线程创建和销毁的开销。
线程池的优势
- 减少线程创建和销毁的开销:线程可以重用
- 控制线程数量:避免线程过多导致系统资源耗尽
- 提高响应速度:线程池中的线程可以立即执行任务
- 管理线程生命周期:统一管理线程的创建、执行和销毁
使用 ExecutorService 创建线程池
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交任务
for (int i = 1; i <= 5; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskId);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 完成任务 " + taskId);
});
}
// 关闭线程池
executor.shutdown();
}
}常见问题
1. 直接调用 run() 方法
症状:线程没有并发执行,而是串行执行
解决方案:使用 start() 方法启动线程,而不是直接调用 run() 方法
示例:
java
// 错误:直接调用 run() 方法
// thread.run();
// 正确:使用 start() 方法
thread.start();2. 多次调用 start() 方法
症状:抛出 IllegalThreadStateException
解决方案:一个线程只能启动一次,不能多次调用 start() 方法
示例:
java
// 错误:多次调用 start() 方法
// thread.start();
// thread.start(); // 抛出异常
// 正确:只调用一次 start() 方法
thread.start();3. 线程安全问题
症状:多个线程同时访问共享资源时,数据不一致
解决方案:使用同步机制,如 synchronized 关键字、Lock 接口等
示例:
java
// 同步代码块
synchronized (lock) {
// 访问共享资源
}
// 同步方法
synchronized void method() {
// 访问共享资源
}最佳实践
优先使用实现 Runnable 接口:更加灵活,避免单继承的限制
使用线程池:对于大量短期任务,使用线程池可以提高性能
避免直接创建线程:对于生产环境,推荐使用线程池管理线程
处理异常:在线程的 run() 方法中捕获并处理异常
设置线程名称:为线程设置有意义的名称,便于调试和监控
避免线程安全问题:对于共享资源,使用适当的同步机制
合理设置线程优先级:根据任务的重要性设置线程优先级
关闭线程池:使用完毕后关闭线程池,避免资源泄漏
总结
创建线程是 Java 并发编程的基础:
创建线程的方法:
- 继承 Thread 类
- 实现 Runnable 接口
- 使用 Lambda 表达式
- 使用 Callable 和 Future
继承 Thread vs 实现 Runnable:
- 实现 Runnable 接口更加灵活,推荐使用
- 继承 Thread 类代码更简洁
资源共享:
- 实现 Runnable 接口可以轻松实现资源共享
- 继承 Thread 类需要使用静态变量实现资源共享
线程池:
- 重用线程,减少开销
- 控制线程数量
- 提高响应速度
常见问题:
- 直接调用 run() 方法
- 多次调用 start() 方法
- 线程安全问题
通过合理选择创建线程的方法,可以有效地实现并发编程,提高程序的性能和响应速度。
