一、线程初始化方式
在Java中,线程的初始化主要有四种方式:
1. 继承Thread类
通过继承Thread类并重写其run()方法,可以创建并启动一个线程。这种方式简单直观,但Java不支持多继承,因此如果一个类已经继承了其他类,就不能再继承Thread类。
代码语言:java复制public class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread is running.");
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程
}
}
2. 实现Runnable接口
通过实现Runnable接口并重写其run()方法,也可以创建线程。这种方式更加灵活,因为Java类可以实现多个接口。但是,与继承Thread类一样,这种方式也无法直接获取线程的运算结果。
代码语言:java复制public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("MyRunnable is running.");
}
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start(); // 启动线程
}
}
3. 实现Callable接口 FutureTask
Callable接口与Runnable接口类似,但Callable可以返回执行结果,并且可以声明抛出异常。FutureTask类实现了Future和Runnable接口,它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。这种方式虽然可以获取线程的运算结果并处理异常,但不利于控制服务器中的线程资源,可能导致服务器资源耗尽。
代码语言:java复制import java.util.concurrent.*;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "MyCallable result";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
executor.submit(futureTask);
// 主线程等待子线程执行完毕并获取结果
String result = futureTask.get();
System.out.println(result); // 输出 "MyCallable result"
executor.shutdown(); // 关闭线程池
}
}
4. 使用线程池
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的ThreadFactory创建一个新线程。通过线程池,我们可以更好地控制和管理线程资源,提高系统性能和稳定性。
代码语言: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 = 0; i < 5; i ) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " taskId " is running by " Thread.currentThread().getName());
});
}
executor.shutdown(); // 关闭线程池
}
}
二、线程池的七大参数
在Java中,线程池的核心是ThreadPoolExecutor类,它提供了七个参数来配置线程池的行为:
- corePoolSize:核心线程数,即线程池中的常驻线程数。即使这些线程在空闲时也不会被销毁。
- maximumPoolSize:线程池允许的最大线程数。当队列满了且已创建的线程数小于maximumPoolSize,则线程池会再创建新的线程执行任务。
- keepAliveTime:当线程数大于核心线程数时,这是多余的空闲线程在终止前等待新任务的最长时间。
- unit:keepAliveTime参数的时间单位,通常是TimeUnit.MILLISECONDS。
- workQueue:用于保存等待执行的任务的阻塞队列。
- threadFactory:用于创建新线程的线程工厂,可以通过自定义的ThreadFactory来创建具有特定名称、优先级、是否为守护线程等的线程。
- handler:当线程池无法处理新任务时使用的饱和策略。
import java.util.concurrent.*;
public class ThreadPoolExecutorExample {
public static void main(String[] args) {
int corePoolSize = 2; // 核心线程数
int maximumPoolSize = 5; // 最大线程数
long keepAliveTime = 60L; // 空闲线程的存活时间
TimeUnit unit = TimeUnit.SECONDS; // 时间单位
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10); // 任务队列
ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 线程工厂
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
// 提交任务到线程池
executor.execute(() -> System.out.println("Task executed by thread pool."));
executor.shutdown(); // 关闭线程池
}
}
三、常见的线程池类型
Java的java.util.concurrent
包提供了几种常见的线程池实现:
FixedThreadPool:固定大小的线程池,它的核心线程数和最大线程数都是指定的,并且工作队列没有大小限制。
代码语言:txt复制ExecutorService executor = Executors.newFixedThreadPool(5); // 创建固定大小的线程池
CachedThreadPool:可缓存的线程池,它的核心线程数为0,最大线程数为Integer.MAX_VALUE,工作队列是SynchronousQueue。这种线程池适合执行大量的耗时较少的任务。
代码语言:txt复制ExecutorService executor = Executors.newCachedThreadPool(); // 创建可缓存的线程池
ScheduledThreadPool:定长线程池,支持定时及周期性任务执行。
代码语言:txt复制ExecutorService executor = Executors.newCachedThreadPool(); // 创建可缓存的线程池
SingleThreadExecutor:单线程化的Executor,它使用单一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
代码语言:txt复制ExecutorService executor = Executors.newSingleThreadExecutor(); // 创建单线程化的Executor