Thread
类。Runnable
接口。Callable
接口(带返回值)。在没有线程池的情况下,创建线程主要有以下几种方式:
Thread
类class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程
}
}
Thread
类并重写 run()
方法。Thread
。Runnable
接口class MyTask implements Runnable {
@Override
public void run() {
System.out.println("Task running: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new MyTask());
thread.start(); // 启动线程
}
}
Thread
对象并将 Runnable
实例传递给它。Callable
接口import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("Task running: " + Thread.currentThread().getName());
return 42; // 返回结果
}
}
public class Main {
public static void main(String[] args) throws Exception {
FutureTask<Integer> futureTask = new FutureTask<>(new MyTask());
Thread thread = new Thread(futureTask);
thread.start();
System.out.println("Result: " + futureTask.get()); // 获取返回值
}
}
Callable
接口允许任务返回结果,并可以抛出异常。FutureTask
配合使用。这是最基础的多线程实现方式,通过手动创建多个线程对象,并调用 start()
方法启动线程。每个线程独立运行,任务逻辑由线程执行。
Thread
类或 Runnable
接口定义线程的任务逻辑。start()
方法,启动线程并执行任务。Thread
类class MyThread extends Thread {
@Override
public void run() {
// 线程的任务逻辑
System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
}
}
public class Main {
public static void main(String[] args) {
// 创建多个线程对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
// 启动线程
t1.start(); // 启动线程 1
t2.start(); // 启动线程 2
}
}
输出示例(顺序可能不同,因为线程是并发执行的):
线程 Thread-0 正在运行
线程 Thread-1 正在运行
Runnable
接口class MyTask implements Runnable {
@Override
public void run() {
// 线程的任务逻辑
System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
}
}
public class Main {
public static void main(String[] args) {
// 创建多个线程对象
Thread t1 = new Thread(new MyTask());
Thread t2 = new Thread(new MyTask());
// 启动线程
t1.start(); // 启动线程 1
t2.start(); // 启动线程 2
}
}
输出示例(顺序可能不同):
线程 Thread-0 正在运行
线程 Thread-1 正在运行
如果任务逻辑简单,可以直接使用匿名内部类来简化代码:
public class Main {
public static void main(String[] args) {
// 使用匿名内部类创建线程
new Thread(() -> {
System.out.println("线程 1 正在运行");
}).start();
new Thread(() -> {
System.out.println("线程 2 正在运行");
}).start();
}
}
输出示例(顺序可能不同):
线程 1 正在运行
线程 2 正在运行
start()
方法会启动线程,并自动调用 run()
方法。run()
方法,否则不会启动新线程,而是在当前线程中执行任务。线程池是一种更高效的多线程实现方式,通过预先创建一组线程并复用来执行任务,避免频繁创建和销毁线程。
Executors
工具类快速创建线程池,或者使用 ThreadPoolExecutor
自定义线程池。Runnable
或 Callable
)提交给线程池。shutdown()
方法优雅地关闭线程池。Executors
工具类import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
// 创建固定大小为 2 的线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
// 提交任务
pool.submit(() -> {
System.out.println("任务 1 正在运行,线程:" + Thread.currentThread().getName());
});
pool.submit(() -> {
System.out.println("任务 2 正在运行,线程:" + Thread.currentThread().getName());
});
// 关闭线程池
pool.shutdown();
}
}
输出示例(顺序可能不同):
任务 1 正在运行,线程:pool-1-thread-1
任务 2 正在运行,线程:pool-1-thread-2
ThreadPoolExecutor
自定义线程池import java.util.concurrent.*;
public class Main {
public static void main(String[] args) {
// 核心线程数
int corePoolSize = 2;
// 最大线程数
int maximumPoolSize = 4;
// 空闲线程存活时间
long keepAliveTime = 60L;
TimeUnit unit = TimeUnit.SECONDS;
// 任务队列
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(10);
// 线程工厂
ThreadFactory threadFactory = Executors.defaultThreadFactory();
// 拒绝策略
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
// 提交任务
for (int i = 1; i <= 5; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("任务 " + taskId + " 正在运行,线程:" + Thread.currentThread().getName());
});
}
// 关闭线程池
executor.shutdown();
}
}
输出示例(顺序可能不同):
任务 1 正在运行,线程:pool-1-thread-1
任务 2 正在运行,线程:pool-1-thread-2
任务 3 正在运行,线程:pool-1-thread-1
任务 4 正在运行,线程:pool-1-thread-2
任务 5 正在运行,线程:pool-1-thread-1
shutdown()
方法可以等待所有任务完成后关闭线程池。shutdownNow()
方法会尝试立即停止所有任务。直接创建多个线程适合小规模任务,使用线程池适合大规模任务或需要高效管理线程的场景。
Executors.newFixedThreadPool(n)
。Executors.newSingleThreadExecutor()
。Executors.newCachedThreadPool()
。Executors.newScheduledThreadPool(n)
。在 Java 中,java.util.concurrent
包提供了多种方式来创建和管理线程池,以下是几种常见的线程池创建方法:
Executors
工具类Executors
是一个线程池工厂类,提供了多种静态方法来创建不同类型的线程池。这些方法简单易用,但在某些场景下可能导致资源浪费或性能问题(如固定大小的线程池可能导致任务堆积)。
ExecutorService executor = Executors.newFixedThreadPool(5);
ExecutorService executor = Executors.newSingleThreadExecutor();
ExecutorService executor = Executors.newCachedThreadPool();
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
ThreadPoolExecutor
手动创建线程池虽然 Executors
提供了便捷的线程池创建方法,但在实际开发中,推荐使用 ThreadPoolExecutor
来手动创建线程池,因为它提供了更细粒度的配置选项。
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
// 核心线程数
int corePoolSize = 5;
// 最大线程数
int maximumPoolSize = 10;
// 空闲线程存活时间
long keepAliveTime = 60L;
// 时间单位
TimeUnit unit = TimeUnit.SECONDS;
// 任务队列
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);
// 线程工厂
ThreadFactory threadFactory = Executors.defaultThreadFactory();
// 拒绝策略
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
// 提交任务
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
});
}
// 关闭线程池
executor.shutdown();
}
}
keepAliveTime
的时间单位。当线程池和任务队列都满时,新的任务将被拒绝,此时会触发拒绝策略。以下是几种常见的拒绝策略:
RejectedExecutionException
异常。当线程池和任务队列都已满时(即:1. 线程池中的线程数已经达到最大线程数。2. 任务队列已满),新的任务无法被处理。此时,线程池会根据配置的 拒绝策略 决定如何处理新提交的任务。
AbortPolicy
(默认策略)RejectedExecutionException
异常,表示任务被拒绝。ExecutorService executor = new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 默认策略
);
executor.submit(() -> {
try {
Thread.sleep(1000); // 模拟任务耗时
} catch (InterruptedException e) {}
System.out.println("Task 1");
});
executor.submit(() -> System.out.println("Task 2"));
executor.submit(() -> System.out.println("Task 3")); // 超过容量,触发拒绝策略
Task 1
Task 2
Exception in thread "main" java.util.concurrent.RejectedExecutionException: ...
CallerRunsPolicy
ExecutorService executor = new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
executor.submit(() -> {
try {
Thread.sleep(1000); // 模拟任务耗时
} catch (InterruptedException e) {}
System.out.println("Task 1");
});
executor.submit(() -> System.out.println("Task 2"));
executor.submit(() -> System.out.println("Task 3")); // 超过容量,由主线程执行
Task 1
Task 2
Task 3
DiscardPolicy
ExecutorService executor = new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy()
);
executor.submit(() -> {
try {
Thread.sleep(1000); // 模拟任务耗时
} catch (InterruptedException e) {}
System.out.println("Task 1");
});
executor.submit(() -> System.out.println("Task 2"));
executor.submit(() -> System.out.println("Task 3")); // 被丢弃,无任何提示
Task 1
Task 2
DiscardOldestPolicy
ExecutorService executor = new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
executor.submit(() -> {
try {
Thread.sleep(1000); // 模拟任务耗时
} catch (InterruptedException e) {}
System.out.println("Task 1");
});
executor.submit(() -> System.out.println("Task 2"));
executor.submit(() -> System.out.println("Task 3")); // 触发 DiscardOldestPolicy
Task 1
Task 3
拒绝策略会在以下情况下触发:
策略名称 | 行为 | 适用场景 |
---|---|---|
AbortPolicy |
抛出异常,拒绝任务 | 需要快速发现问题并对异常进行处理的场景 |
CallerRunsPolicy |
由调用线程执行任务 | 允许任务稍有延迟,但不能丢失的场景 |
DiscardPolicy |
静默丢弃任务 | 对任务丢失不敏感的场景 |
DiscardOldestPolicy |
丢弃最老的任务,重试新任务 | 新任务比旧任务更重要的场景 |
避免使用 Executors
默认线程池
Executors
提供的线程池在某些场景下可能导致内存泄漏或性能问题,例如 newFixedThreadPool
和 newSingleThreadExecutor
使用的是无界队列,可能会导致任务堆积,最终耗尽内存。
合理设置线程池参数
根据任务类型(CPU 密集型或 I/O 密集型)和硬件资源(CPU 核心数)合理设置线程池大小。例如:
及时关闭线程池
使用完线程池后,务必调用 shutdown()
或 shutdownNow()
方法释放资源。
在线程池中,任务的提交方式主要依赖于 Runnable
和 Callable
接口,而不是直接创建线程。以下是两种常见的任务提交方式:
Runnable
任务ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> {
System.out.println("Task running: " + Thread.currentThread().getName());
});
executor.shutdown();
Runnable
任务不返回结果,适用于不需要获取任务执行结果的场景。Callable
任务ExecutorService executor = Executors.newFixedThreadPool(5);
Future<Integer> future = executor.submit(() -> {
System.out.println("Task running: " + Thread.currentThread().getName());
return 42; // 返回结果
});
System.out.println("Result: " + future.get()); // 获取返回值
executor.shutdown();
Callable
任务返回结果,适用于需要获取任务执行结果的场景。Future
对象用于获取任务的返回值或检查任务状态。当使用线程池时,不需要手动创建线程。线程池已经为你管理了线程的生命周期,你只需要关注任务逻辑即可。具体来说:
Runnable
接口,可以通过 submit()
或 execute()
方法提交任务。Callable
接口,可以通过 submit()
方法提交任务,并通过 Future
获取结果。换句话说,在使用线程池的情况下,传统的创建线程的方式(如继承 Thread
类或直接创建 Thread
对象)已经被线程池的机制所取代。
性能优化
线程的创建和销毁开销较大,线程池通过复用线程显著减少了这种开销。
资源控制
线程池可以限制并发线程数,避免系统资源耗尽。
任务管理
线程池提供了任务队列、拒绝策略等机制,方便管理和调度任务。
代码简洁
使用线程池后,代码更加清晰,无需手动管理线程的生命周期。
线程是基础,多线程是目标,线程池是实现多线程的高效工具。