Thread 类是 Java 提供的用于创建和控制线程的类。通过继承该类并重写其 run() 方法,可以创建一个新线程。
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running.");
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程
}
}
实现Runnable接口创建线程
Runnable 接口是另一种常用的创建线程的方式。实现 Runnable 接口并重写 run() 方法,再通过 Thread 类来启动线程。
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread is running.");
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start(); // 启动线程
}
}
通过Callable和FutureTask创建线程
Callable 是一个类似于 Runnable 的接口,但它允许任务有返回值,并且可以抛出异常。结合 Future 接口,可以获取任务执行的结果
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 42; // 返回值
}
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<Integer> future = executorService.submit(new MyCallable());
System.out.println("Result: " + future.get()); // 获取任务的返回值
executorService.shutdown();
}
}
通过线程池创建线程
线程池通过预先创建多个线程并复用这些线程来处理任务,从而减少了频繁创建和销毁线程的开销。线程池的使用更加高效,并且可以有效地控制并发数量,避免线程过多导致资源耗尽。(比较推荐)
1. 使用 Executors 工厂方法创建线程池
Java 提供了几个 Executors 工厂方法来创建不同类型的线程池。常见的有:
1.newFixedThreadPool(int nThreads):创建一个固定大小的线程池。
2.newCachedThreadPool():创建一个可缓存的线程池,线程池的大小是动态的,根据需求创建新线程。
3.newSingleThreadExecutor():创建一个只有一个线程的线程池。
4.newScheduledThreadPool(int corePoolSize):创建一个可以定时执行任务的线程池。
创建一个固定大小线程池的代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建一个固定大小的线程池,大小为 3
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 提交多个任务到线程池
for (int i = 0; i < 5; i++) {
executorService.submit(new RunnableTask(i));
}
// 关闭线程池
executorService.shutdown();
}
}
class RunnableTask implements Runnable {
private int taskId;
public RunnableTask(int taskId) {
this.taskId = taskId;
}
@Override
public void run() {
System.out.println("Task " + taskId + " is being executed by " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟任务执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Executors.newFixedThreadPool(3) 创建一个线程池,最多同时运行 3 个线程。如果任务数量超过线程池大小,剩余的任务将等待有线程空闲时执行。
executorService.submit(new RunnableTask(i)) 提交任务到线程池。
executorService.shutdown() 用于关闭线程池,当所有任务完成后,线程池会被关闭。
创建一个缓存的线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPoolExample {
public static void main(String[] args) {
// 创建一个可缓存的线程池
ExecutorService executorService = Executors.newCachedThreadPool();
// 提交多个任务到线程池
for (int i = 0; i < 5; i++) {
executorService.submit(new RunnableTask(i));
}
// 关闭线程池
executorService.shutdown();
}
}
class RunnableTask implements Runnable {
private int taskId;
public RunnableTask(int taskId) {
this.taskId = taskId;
}
@Override
public void run() {
System.out.println("Task " + taskId + " is being executed by " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟任务执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Executors.newCachedThreadPool() 创建一个线程池,该池的线程数可以根据任务需求动态增加,线程池中没有任务时会回收空闲线程。适用于执行短时间的异步任务,特别是当任务数量不可预测时。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadPoolExample {
public static void main(String[] args) {
// 创建一个线程池,核心线程数为 2
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 创建一个任务
Runnable task = () -> System.out.println("Task executed at: " + System.currentTimeMillis());
// 定期每 3 秒执行一次任务
scheduler.scheduleAtFixedRate(task, 0, 3, TimeUnit.SECONDS);
// 或者,延迟执行任务,任务将在 5 秒后开始执行
// scheduler.schedule(task, 5, TimeUnit.SECONDS);
}
}
corePoolSize:指定线程池的核心线程数。即使在空闲状态下,核心线程也会保持在池中,直到线程池关闭。这个参数决定了线程池中至少会有多少个线程来处理任务。
ScheduledExecutorService:该线程池支持定时和周期性任务调度,可以通过 schedule() 方法来延迟执行任务,或者通过 scheduleAtFixedRate() 和 scheduleWithFixedDelay() 来定期执行任务。
2.自定义线程池
自定义线程池是根据需求来定制线程池的一种方式。通常,我们可以通过 ThreadPoolExecutor 来实现线程池的自定义,而不使用 Executors 工厂方法。ThreadPoolExecutor 提供了丰富的参数配置,使得我们可以灵活地创建符合需求的线程池。
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
corePoolSize:核心线程池大小,即线程池中始终保持的最小线程数。
maximumPoolSize:线程池能够创建的最大线程数。如果任务量大于核心线程数,线程池会创建更多的线程,直到达到这个最大值。
keepAliveTime:当线程池中的线程数超过核心线程数时,多余的线程在空闲时间达到 keepAliveTime 后会被终止。这个时间单位由 unit 参数指定。
unit:keepAliveTime 的时间单位,例如 TimeUnit.SECONDS、TimeUnit.MILLISECONDS 等。
workQueue:用于保存待执行任务的队列。常见的队列有 ArrayBlockingQueue(固定大小队列)、LinkedBlockingQueue(无限队列)、PriorityBlockingQueue(优先级队列)等。
示例:自定义线程池
import java.util.concurrent.*;
public class CustomThreadPool {
public static void main(String[] args) {
// 创建一个核心线程数为 2,最大线程数为 4,空闲线程存活时间为 60 秒,队列为无界队列的线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2, // corePoolSize
4, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS, // keepAliveTime单位
new LinkedBlockingQueue<>() // workQueue
);
// 创建一个任务
Runnable task = () -> {
System.out.println("Task executed by " + Thread.currentThread().getName());
};
// 提交 5 个任务到线程池
for (int i = 0; i < 5; i++) {
threadPool.submit(task);
}
// 关闭线程池
threadPool.shutdown();
}
}
线程池创建:
核心线程数设置为 2,即线程池中始终保持 2 个线程。
最大线程数设置为 4,意味着在高负载情况下,最多可以创建 4 个线程。
空闲线程在超过 60 秒没有任务时会被终止。
使用 LinkedBlockingQueue 作为任务队列,表示队列是一个无界队列,任务可以无限入队,直到线程池的最大线程数达到限制。
任务提交:
创建了一个任务,该任务只是输出当前线程的名字。
使用 submit() 方法将任务提交给线程池。
关闭线程池:
使用 shutdown() 方法优雅地关闭线程池。它会等待线程池中的所有任务执行完毕后才会关闭线程池。
其他自定义线程池参数
拒绝策略和线程工厂
拒绝策略
当线程池无法处理更多任务时,可以设置拒绝策略。例如,使用 RejectedExecutionHandler 来定义任务被拒绝时的处理方式。常见的拒绝策略有:
ThreadPoolExecutor.AbortPolicy(默认):丢弃任务并抛出 RejectedExecutionException。
ThreadPoolExecutor.DiscardPolicy:丢弃任务但不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列中最旧的任务,然后重试提交新任务。
ThreadPoolExecutor.CallerRunsPolicy:由提交任务的线程来执行任务,而不是丢弃任务。
示例:带拒绝策略的线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2, // corePoolSize
4, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS, // keepAliveTime单位
new LinkedBlockingQueue<>(2), // workQueue,容量为2的队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:由调用者线程执行任务
);
线程工厂
示例:带线程工厂的线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2, // corePoolSize
4, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS, // keepAliveTime单位
new LinkedBlockingQueue<>(), // workQueue
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("Custom-Thread-" + thread.getId()); // 设置线程名称
return thread;
}
}
);
总结
自定义线程池通过 ThreadPoolExecutor 提供了高度的灵活性,允许你根据应用需求调整线程池的核心线程数、最大线程数、队列类型、拒绝策略等。常见的用途包括:
高并发任务的调度。
限制最大线程数避免过多线程导致性能瓶颈。
实现自定义任务队列来管理任务的执行顺序。
通过调整线程池的各个参数,你可以创建适应不同场景的线程池。
总结
其实归根结底就两种,一个是继承Thread类,一个是实现Runnable接口,至于其他的,也是基于这两个方法实现的。