在 Java 中,创建多线程的方式主要有以下几种:
-
继承
Thread类
通过继承 Thread 类并重写 run() 方法来创建线程。
示例代码:
class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程运行中: " + Thread.currentThread().getName());}
}public class Main {public static void main(String[] args) {MyThread thread = new MyThread();thread.start(); // 启动线程}
}
特点:
-
简单易用,适合简单的多线程任务。
-
由于 Java 是单继承,继承
Thread类后会占用继承名额,不够灵活。
2. 实现 Runnable 接口
通过实现 Runnable 接口并实现 run() 方法来创建线程。
示例代码:
class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程运行中: " + Thread.currentThread().getName());}
}public class Main {public static void main(String[] args) {Thread thread = new Thread(new MyRunnable());thread.start(); // 启动线程}
}
特点:
-
更灵活,因为可以实现多个接口,而不受单继承的限制。
-
适合需要共享资源的场景,因为多个线程可以共享同一个
Runnable实例。
3. 实现 Callable 接口
通过实现 Callable 接口并实现 call() 方法来创建线程。Callable 可以有返回值,并且可以抛出异常。
示例代码:
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {return "线程运行结果: " + Thread.currentThread().getName();}
}public class Main {public static void main(String[] args) throws Exception {FutureTask<String> futureTask = new FutureTask<>(new MyCallable());Thread thread = new Thread(futureTask);thread.start(); // 启动线程System.out.println(futureTask.get()); // 获取线程返回值}
}
特点:
-
支持返回值,适合需要获取线程执行结果的场景。
-
可以抛出异常,方便错误处理。
4. 使用线程池(ExecutorService)
通过线程池管理线程的创建和执行,避免频繁创建和销毁线程的开销。
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Main {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(5); // 创建固定大小的线程池for (int i = 0; i < 10; i++) {executor.execute(() -> {System.out.println("线程运行中: " + Thread.currentThread().getName());});}executor.shutdown(); // 关闭线程池}
}
特点:
-
高效管理线程资源,适合需要处理大量任务的场景。
-
支持多种线程池类型(如固定大小、缓存、定时任务等)。
5. 使用 Lambda 表达式(Java 8+)
通过 Lambda 表达式简化 Runnable 或 Callable 的实现。
示例代码:
public class Main {public static void main(String[] args) {// 使用 Lambda 创建 RunnableThread thread = new Thread(() -> {System.out.println("线程运行中: " + Thread.currentThread().getName());});thread.start();}
}
特点:
- 代码简洁,适合简单的多线程任务。
6. 使用 CompletableFuture(Java 8+)
通过 CompletableFuture 实现异步编程,支持链式调用和组合多个异步任务。
示例代码:
import java.util.concurrent.CompletableFuture;public class Main {public static void main(String[] args) {CompletableFuture.runAsync(() -> {System.out.println("异步任务运行中: " + Thread.currentThread().getName());}).join(); // 等待任务完成}
}
特点:
-
支持异步编程和任务组合。
-
适合复杂的多线程场景。
7. 使用 ForkJoinPool(Java 7+)
通过 ForkJoinPool 实现分治任务,适合处理可以拆分的任务。
示例代码:
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;class MyTask extends RecursiveAction {@Overrideprotected void compute() {System.out.println("分治任务运行中: " + Thread.currentThread().getName());}
}public class Main {public static void main(String[] args) {ForkJoinPool pool = new ForkJoinPool();pool.invoke(new MyTask()); // 执行任务}
}
特点:
-
适合处理可以拆分的任务(如递归任务)。
-
自动利用多核 CPU 的优势。
总结
Java 中创建多线程的方式主要有以下几种:
-
继承
Thread类 -
实现
Runnable接口 -
实现
Callable接口 -
使用线程池(
ExecutorService) -
使用 Lambda 表达式
-
使用
CompletableFuture -
使用
ForkJoinPool
每种方式都有其适用场景,推荐根据具体需求选择合适的方式。对于现代 Java 开发,线程池和 CompletableFuture 是更推荐的选择,因为它们更高效、更灵活。
线程池是 Java 并发编程中非常重要的工具,它通过复用线程来减少线程创建和销毁的开销,从而提高系统性能。线程池的核心实现类是 ThreadPoolExecutor,其构造函数有七个核心参数,这些参数决定了线程池的行为和特性。
线程池****的七个核心参数
1. corePoolSize(核心线程数)
-
含义:线程池中保持存活的最小线程数,即使这些线程处于空闲状态。
-
特点:
-
线程池初始化时,默认没有线程,当有任务提交时才会创建线程。
-
如果设置了
allowCoreThreadTimeOut为true,核心线程在空闲时也会被回收。
-
2. maximumPoolSize(最大线程数)
-
含义:线程池中允许存在的最大线程数。
-
特点:
-
当任务数量超过核心线程数,并且工作队列已满时,线程池会创建新线程,直到线程数达到
maximumPoolSize。 -
如果任务数量继续增加,线程池会触发拒绝策略。
-
3. keepAliveTime(线程空闲时间)
-
含义:当线程池中的线程数量超过
corePoolSize时,空闲线程的存活时间。 -
特点:
-
如果线程空闲时间超过
keepAliveTime,多余的线程会被回收,直到线程数等于corePoolSize。 -
如果
allowCoreThreadTimeOut为true,核心线程也会被回收。
-
4. unit(时间单位)
-
含义:
keepAliveTime的时间单位。 -
常用单位:
-
TimeUnit.MILLISECONDS(毫秒) -
TimeUnit.SECONDS(秒) -
TimeUnit.MINUTES(分钟)
-
5. workQueue(工作队列)
-
含义:用于存放待执行任务的阻塞队列。
-
常用队列类型:
-
LinkedBlockingQueue:无界队列(默认),任务数量不受限制,可能导致内存溢出。 -
ArrayBlockingQueue:有界队列,任务数量受队列容量限制。 -
SynchronousQueue:不存储任务的队列,任务直接提交给线程执行。 -
PriorityBlockingQueue:优先级队列,任务按优先级执行。
-
6. threadFactory(线程工厂)
-
含义:用于创建新线程的工厂。
-
特点:
-
可以自定义线程的名称、优先级、是否为守护线程等。
-
默认使用
Executors.defaultThreadFactory()。
-
7. handler(拒绝策略)
-
含义:当线程池无法处理新任务时的拒绝策略。
-
常用策略:
-
AbortPolicy(默认):直接抛出RejectedExecutionException异常。 -
CallerRunsPolicy:由提交任务的线程直接执行该任务。 -
DiscardPolicy:直接丢弃任务,不抛出异常。 -
DiscardOldestPolicy:丢弃队列中最旧的任务,然后重新提交当前任务。
-
线程池****的工作流程
-
当提交一个新任务时:
-
如果当前线程数 <
corePoolSize,创建新线程执行任务。 -
如果当前线程数 >=
corePoolSize,将任务放入工作队列。 -
如果工作队列已满且当前线程数 <
maximumPoolSize,创建新线程执行任务。 -
如果工作队列已满且当前线程数 >=
maximumPoolSize,触发拒绝策略。
-
-
当线程空闲时间超过
keepAliveTime时:-
如果当前线程数 >
corePoolSize,回收多余线程。 -
如果
allowCoreThreadTimeOut为true,核心线程也会被回收。
-
示例代码
import java.util.concurrent.*;public class ThreadPoolExample {public static void main(String[] args) {// 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(2, // corePoolSize5, // maximumPoolSize60, // keepAliveTimeTimeUnit.SECONDS, // unitnew LinkedBlockingQueue<>(10), // workQueueExecutors.defaultThreadFactory(), // threadFactorynew ThreadPoolExecutor.AbortPolicy() // handler);// 提交任务for (int i = 0; i < 15; i++) {int taskId = i;executor.execute(() -> {System.out.println("任务 " + taskId + " 正在执行,线程: " + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池executor.shutdown();}
}
总结
线程池的七个核心参数决定了线程池的行为和性能:
-
corePoolSize:核心线程数。 -
maximumPoolSize:最大线程数。 -
keepAliveTime:线程空闲时间。 -
unit:时间单位。 -
workQueue:工作队列。 -
threadFactory:线程工厂。 -
handler:拒绝策略。
合理配置这些参数可以优化线程池的性能,避免资源浪费和任务丢失。
在 Java 中,线程的状态是通过 Thread.State 枚举类来表示的。线程在其生命周期中会经历不同的状态,这些状态反映了线程当前的活动情况。以下是 Java 中线程的六种状态:
1. NEW(新建)
-
描述:线程被创建但尚未启动。
-
触发条件:
- 调用
new Thread()创建线程对象,但尚未调用start()方法。
- 调用
-
示例:
Thread thread = new Thread(() -> {}); System.out.println(thread.getState()); // 输出: NEW
2. RUNNABLE****(可运行)
-
描述:线程正在 JVM 中执行,但可能正在等待操作系统资源(如 CPU)。
-
触发条件:
-
调用
start()方法后,线程进入 RUNNABLE 状态。 -
线程可能正在运行,也可能在等待 CPU 时间片。
-
-
示例:
Thread thread = new Thread(() -> {while (true) {} }); thread.start(); System.out.println(thread.getState()); // 输出: RUNNABLE
3. BLOCKED(阻塞)
-
描述:线程被阻塞,等待获取监视器锁(monitor lock)。
-
触发条件:
- 线程试图进入一个被其他线程持有的
synchronized代码块或方法。
- 线程试图进入一个被其他线程持有的
-
示例:
Object lock = new Object();Thread thread1 = new Thread(() -> {synchronized (lock) {while (true) {}} });Thread thread2 = new Thread(() -> {synchronized (lock) {System.out.println("Thread 2 获取锁");} });thread1.start(); Thread.sleep(100); // 确保 thread1 先获取锁 thread2.start(); Thread.sleep(100); // 确保 thread2 进入 BLOCKED 状态 System.out.println(thread2.getState()); // 输出: BLOCKED
4. WAITING(等待)
-
描述:线程无限期等待,直到被其他线程显式唤醒。
-
触发条件:
- 调用
Object.wait()、Thread.join()或LockSupport.park()方法。
- 调用
-
示例:
Thread thread = new Thread(() -> {synchronized (Thread.currentThread()) {try {Thread.currentThread().wait(); // 进入 WAITING 状态} catch (InterruptedException e) {e.printStackTrace();}} });thread.start(); Thread.sleep(100); // 确保线程进入 WAITING 状态 System.out.println(thread.getState()); // 输出: WAITING
5. TIMED_WAITING(超时等待)
-
描述:线程在指定的时间内等待,超时后自动唤醒。
-
触发条件:
- 调用
Thread.sleep(long)、Object.wait(long)、Thread.join(long)或LockSupport.parkNanos()等方法。
- 调用
-
示例:
Thread thread = new Thread(() -> {try {Thread.sleep(1000); // 进入 TIMED_WAITING 状态} catch (InterruptedException e) {e.printStackTrace();} });thread.start(); Thread.sleep(100); // 确保线程进入 TIMED_WAITING 状态 System.out.println(thread.getState()); // 输出: TIMED_WAITING
6. TERMINATED****(终止)
-
描述:线程已经执行完毕,或者因异常退出。
-
触发条件:
-
线程的
run()方法执行完毕。 -
线程因未捕获的异常而终止。
-
-
示例:
Thread thread = new Thread(() -> {System.out.println("线程执行完毕"); });thread.start(); thread.join(); // 等待线程执行完毕 System.out.println(thread.getState()); // 输出: TERMINATED
线程状态转换图
以下是线程状态的典型转换关系:
NEW → RUNNABLE → BLOCKED → RUNNABLE → TERMINATED↓ ↓WAITING → RUNNABLE↓TIMED_WAITING → RUNNABLE
总结
Java 线程的六种状态和特点:
-
NEW:线程被创建但未启动。
-
RUNNABLE:线程正在运行或等待 CPU 资源。
-
BLOCKED:线程等待获取锁。
-
WAITING:线程无限期等待。
-
TIMED_WAITING:线程在指定时间内等待。
-
TERMINATED:线程执行完毕或异常终止。
