线程有哪几种状态?
从源码看:
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}

- NEW(新建状态):Thread对象被创建出来了,但是还没有执行start方法。
- RUNNABLE(运行或就绪状态):Thread对象调用了start方法,就为RUNNABLE状态(CPU调度/没有调度)
- BLOCKED(阻塞状态)、WAITING(等待状态)、TIME_WAITING(时间等待状态):都可以理解为是阻塞、等待状态,因为处在这三种状态下,CPU不会调度当前线程
- BLOCKED(阻塞状态):synchronized没有拿到同步锁,被阻塞的情况
- WAITING(等待状态):调用wait方法就会处于WAITING状态,需要被手动唤醒
- TIME_WAITING(时间等待状态):调用sleep方法或者join方法,会被自动唤醒,无需手动唤醒
- TERMINATED(结束状态):run方法执行完毕,线程生命周期到头了
线程的基本方法
start()
start()方法用于启动线程。线程创建以后,并不会自动运行,需要我们调用start(),将线程的状态设为就绪状态,但不一定马上就被运行,得等到CPU分配时间片以后,才会运行
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running");
}
}
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // 启动新线程
}
}
注意:直接调用run()方法不会启动新线程,而是在当前线程中执行run()方法。
run()
run()方法包含线程执行的代码。它是Thread类和Runnable接口的核心方法。
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread is running");
}
}
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start(); // 启动新线程,实际调用的仍是 run() 方法
}
}
sleep(long millis)
sleep(long millis)方法使当前线程休眠指定的毫秒数。它会抛出InterruptedException,因此需要处理该异常。
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
System.out.println("Thread is sleeping");
Thread.sleep(1000); // 休眠1秒
System.out.println("Thread woke up");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
}
}
join()
join()方法等待线程终止。调用该方法的线程会等待被调用线程执行完毕后再继续执行。
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000); // 模拟工作
System.out.println("Thread finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
try {
t1.join(); // 等待 t1 线程结束
System.out.println("Main thread continues");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
interrupt()
interrupt()方法用于中断线程。被中断的线程会抛出InterruptedException。
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Thread is running");
Thread.sleep(500);
}
} catch (InterruptedException e) {
System.out.println("Thread was interrupted");
}
});
t1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.interrupt(); // 中断 t1 线程
}
}
isInterrupted()
isInterrupted()方法用于检查线程是否被中断。它返回一个布尔值。
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Thread is interrupted");
break;
}
System.out.println("Thread is running");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新设置中断状态
}
}
});
t1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.interrupt(); // 中断 t1 线程
}
}
setPriority(int newPriority)
setPriority(int newPriority)方法用于设置线程的优先级。优先级范围从Thread.MIN_PRIORITY(1) 到Thread.MAX_PRIORITY(10),默认优先级为Thread.NORM_PRIORITY(5)。
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("Thread is running with priority: " + Thread.currentThread().getPriority());
});
t1.setPriority(Thread.MAX_PRIORITY);
t1.start();
}
}
getPriority()
getPriority()方法用于获取线程的优先级。
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("Thread priority: " + Thread.currentThread().getPriority());
});
t1.start();
}
}
setName(String name)
setName(String name)方法用于设置线程的名称。
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("Thread name: " + Thread.currentThread().getName());
});
t1.setName("MyThread");
t1.start();
}
}
getName()
getName()方法用于获取线程的名称。
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("Thread name: " + Thread.currentThread().getName());
});
t1.start();
}
}
currentThread()
currentThread()方法用于获取当前正在执行的线程。
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
Thread currentThread = Thread.currentThread();
System.out.println("Current thread: " + currentThread.getName());
});
t1.start();
}
}
线程的创建方式
1、继承 Thread
Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方
法就是通过 Thread 类的 start()实例方法。start()方法是一个 native 方法,它将启动一个新线
程,并执行 run()方法。
public class multithreadDemo001 extends Thread{
@Override
public void run() {
// 线程要执行的任务
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getId() + " 正在执行任务 " + i);
try {
// 模拟一些工作
Thread.sleep(1000); // 暂停1秒
} catch (InterruptedException e) {
System.out.println(e);
}
}
}
public static void main(String[] args) {
// 创建两个线程
multithreadDemo001 thread1 = new multithreadDemo001();
multithreadDemo001 thread2 = new multithreadDemo001();
thread1.start();
thread2.start();
}
}
多文件下载器
package com.taiyuan.javademoone.multithreaddemo;
/**
* DownloadThread 类继承自 Thread 类,用于并发下载文件
* 它允许程序在不等待文件下载完成的情况下,继续执行其他任务
*/
public class DownloadThread extends Thread {
private String fileUrl; // 文件的 URL 地址
private String savePath; // 文件保存的路径
/**
* 构造函数,初始化文件的 URL 地址和保存路径
* @param fileUrl 文件的 URL 地址
* @param savePath 文件保存的路径
*/
public DownloadThread(String fileUrl, String savePath) {
this.fileUrl = fileUrl;
this.savePath = savePath;
}
/**
* 线程的运行方法
* 当线程启动时,会执行此方法中的代码
* 主要功能是模拟文件下载过程,并打印下载信息
*/
@Override
public void run() {
System.out.println("开始下载: " + fileUrl);
// 模拟下载过程
try {
Thread.sleep(2000); // 模拟下载所需的时间
System.out.println(fileUrl + " 下载完成,保存到: " + savePath);
} catch (InterruptedException e) {
// 如果线程被中断,则输出中断信息
System.out.println(fileUrl + " 下载被中断");
}
}
/**
* 主方法,用于创建和启动多个 DownloadThread 线程
* 示例了如何同时下载多个文件,并指出主线程可以继续执行其他任务
*/
public static void main(String[] args) {
// 同时下载多个文件
new DownloadThread("http://example.com/file1.zip", "D:/downloads/file1.zip").start();
new DownloadThread("http://example.com/file2.zip", "D:/downloads/file2.zip").start();
new DownloadThread("http://example.com/file3.zip", "D:/downloads/file3.zip").start();
// 主线程继续执行其他任务,显示了并发执行的特点
System.out.println("主线程继续执行其他任务...");
}
}
2、实现 Runnable 接口
如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时,可以实现一个
Runnable 接口。
/**
* MyRunnableDemo类实现了Runnable接口,用于创建执行特定任务的线程
*/
public class MyRunnableDemo implements Runnable {
/**
* run方法定义了线程启动后要执行的操作
* 在这个方法中,线程将执行一系列的任务,每个任务之间有短暂的暂停
*/
@Override
public void run() {
// 线程要执行的任务
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getId() + " 正在执行任务 " + i);
try {
// 模拟一些工作
Thread.sleep(2000); // 暂停1秒
} catch (InterruptedException e) {
System.out.println(e);
}
}
}
public static void main(String[] args) {
// 创建Runnable对象
MyRunnableDemo myRunnable = new MyRunnableDemo();
// 创建两个线程并传入Runnable对象
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
// 启动线程
thread1.start();
thread2.start();
}
}
3、实现 Callable 接口
与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。
package com.taiyuan.javademoone.multithreaddemo;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableDemo implements Callable<Integer>{
@Override
public Integer call() throws Exception {
// 模拟耗时计算
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
Thread.sleep(10); // 模拟耗时操作
}
return sum;
}
public static void main(String[] args) {
// 创建Callable实现类的实例
CallableDemo callable = new CallableDemo();
// 使用FutureTask来包装Callable对象
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
// 创建线程并启动
thread.start();
try {
// 主线程可以做其他工作...
System.out.println("主线程正在执行其他任务...");
// 获取子线程的返回结果(会阻塞直到子线程完成)
Integer result = futureTask.get();
System.out.println("计算结果: " + result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("所有任务完成");
}
}
有返回值的任务必须实现 Callable 接口,类似的,无返回值的任务必须 Runnable 接口。执行Callable 任务后,可以获取一个 Future 的对象,在该对象上调用 get 就可以获取到 Callable 任务返回的 Object 了,再结合线程池接口 ExecutorService 就可以实现传说中有返回结果的多线程了。
案例:订单处理场景
模拟一个电商系统中的订单处理场景,使用 Callable 和 ExecutorService 实现多线程并行处理订单,并收集处理结果。
场景描述
- 系统需要批量处理一批订单
- 每个订单处理是一个独立任务(检查库存、计算价格、生成物流单等)
- 需要获取每个订单的处理结果(成功/失败及详细信息)
- 主线程需要汇总所有订单的处理结果
1、订单实体类
package com.taiyuan.javademoone.OrderProcessingSystem;
/**
* 订单实体类
*/
public class Order {
/**
* 订单ID
* 用于唯一标识一个订单
*/
private String orderId;
/**
* 产品名称
* 描述订单中包含的产品或服务
*/
private String product;
/**
* 数量
* 表示购买的产品或服务的数量
*/
private int quantity;
/**
* 价格
* 记录产品或服务的单价
*/
private double price;
public Order(String orderId, String product, int quantity, double price) {
this.orderId = orderId;
this.product = product;
this.quantity = quantity;
this.price = price;
}
public String getOrderId() {
return orderId;
}
public String getProduct() {
return product;
}
public int getQuantity() {
return quantity;
}
public double getPrice() {
return price;
}
}
2、订单处理结果类
package com.taiyuan.javademoone.OrderProcessingSystem;
/**
* 订单处理结果类
*/
public class OrderResult {
// 订单ID
private String orderId;
// 订单是否成功
private boolean success;
// 订单消息
private String message;
// 订单总金额
private double totalAmount;
public OrderResult(String orderId, boolean success, String message, double totalAmount) {
this.orderId = orderId;
this.success = success;
this.message = message;
this.totalAmount = totalAmount;
}
// Getter方法
public String getOrderId() { return orderId; }
public boolean isSuccess() { return success; }
public String getMessage() { return message; }
public double getTotalAmount() { return totalAmount; }
@Override
public String toString() {
return String.format("订单%s: %s (金额: %.2f) - %s",
orderId,
success ? "成功" : "失败",
totalAmount,
message);
}
}
3、订单处理任务类(实现Callable接口)
package com.taiyuan.javademoone.OrderProcessingSystem;
import java.util.Random;
import java.util.concurrent.Callable;
/**
* 订单处理任务类(实现Callable接口)
*/
class OrderTask implements Callable<OrderResult> {
private final Order order;
public OrderTask(Order order) {
this.order = order;
}
@Override
public OrderResult call() throws Exception {
long startTime = System.currentTimeMillis();
// 模拟订单处理耗时
Thread.sleep(new Random().nextInt(1000));
// 1. 检查库存(随机模拟成功/失败)
boolean hasStock = new Random().nextBoolean();
if (!hasStock) {
long endTime = System.currentTimeMillis();
System.out.println("订单号:"+order.getOrderId()+" 订单处理完成,耗时:" + (endTime - startTime) + "ms");
return new OrderResult(order.getOrderId(), false, "库存不足", 0);
}
// 2. 计算总金额
double total = order.getPrice() * order.getQuantity();
// 3. 模拟其他处理(支付、物流等)
boolean otherCheck = new Random().nextInt(10) > 2; // 80%成功率
if (!otherCheck) {
long endTime = System.currentTimeMillis();
System.out.println("订单号:"+order.getOrderId()+" 订单处理完成,耗时:" + (endTime - startTime) + "ms");
return new OrderResult(order.getOrderId(), false, "支付或物流处理失败", 0);
}
long endTime = System.currentTimeMillis();
System.out.println("订单号:"+order.getOrderId()+" 订单处理完成,耗时:" + (endTime - startTime) + "ms");
// 处理成功
return new OrderResult(order.getOrderId(), true, "订单处理成功", total);
}
}
4、订单处理结果摘要类
package com.taiyuan.javademoone.OrderProcessingSystem;
import java.util.List;
/**
* 订单处理结果摘要类
*/
public class OrderProcessSummary {
// 总订单数
private int totalOrders;
// 成功订单数
private int successCount;
// 失败订单数
private int failureCount;
// 总金额
private double totalAmount;
// 订单结果列表
private List<OrderResult> results;
public OrderProcessSummary(List<OrderResult> results) {
this.results = results;
this.totalOrders = results.size();
this.successCount = (int) results.stream().filter(OrderResult::isSuccess).count();
this.failureCount = totalOrders - successCount;
this.totalAmount = results.stream().mapToDouble(OrderResult::getTotalAmount).sum();
}
// Getter方法
public int getTotalOrders() {
return totalOrders;
}
public int getSuccessCount() {
return successCount;
}
public int getFailureCount() {
return failureCount;
}
public double getTotalAmount() {
return totalAmount;
}
public List<OrderResult> getResults() {
return results;
}
}
5、订单处理器类(核心业务逻辑)
package com.taiyuan.javademoone.OrderProcessingSystem;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 订单处理器类(核心业务逻辑)
*/
public class OrderProcessor {
/**
* 批量处理订单
* @param orders 订单列表
* @return 处理结果摘要
*/
public OrderProcessSummary processOrders(List<Order> orders) {
long startTime = System.currentTimeMillis();
// 创建线程池(根据CPU核心数设置线程数)
int threadCount = Runtime.getRuntime().availableProcessors();
System.out.println("当前设备线程数:" + threadCount);
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
// 提交所有订单处理任务
List<Future<OrderResult>> futures = new ArrayList<>();
for (Order order : orders) {
Future<OrderResult> future = executor.submit(new OrderTask(order));
futures.add(future);
}
// 获取并处理所有订单的结果
List<OrderResult> results = new ArrayList<>();
for (Future<OrderResult> future : futures) {
try {
// get()会阻塞直到任务完成
OrderResult result = future.get();
results.add(result);
System.out.println("订单处理完成: " + result);
} catch (InterruptedException | ExecutionException e) {
System.out.println("订单处理异常: " + e.getMessage());
results.add(new OrderResult("未知", false, "处理异常: " + e.getMessage(), 0));
}
}
// 关闭线程池
executor.shutdown();
long endTime = System.currentTimeMillis();
System.out.println("订单处理完成,耗时: " + (endTime - startTime) + "ms");
return new OrderProcessSummary(results);
}
}
6、订单处理系统演示类
package com.taiyuan.javademoone.OrderProcessingSystem;
import java.util.Arrays;
import java.util.List;
/**
* 订单处理系统演示类
* 展示如何使用Callable和ExecutorService实现有返回值的多线程任务
*/
public class OrderProcessingDemo {
public static void main(String[] args) {
// 创建订单处理器
OrderProcessor processor = new OrderProcessor();
// 准备测试订单数据
List<Order> orders = prepareTestOrders();
// 处理订单并获取结果
OrderProcessSummary summary = processor.processOrders(orders);
// 打印处理结果摘要
printSummary(summary);
}
/**
* 准备测试订单数据
*/
private static List<Order> prepareTestOrders() {
return Arrays.asList(
new Order("1001", "iPhone 15", 1, 7999.00),
new Order("1002", "MacBook Pro", 1, 12999.00),
new Order("1003", "AirPods Pro", 2, 1499.00),
new Order("1004", "iPad Air", 1, 4799.00),
new Order("1005", "Apple Watch", 3, 2999.00)
);
}
/**
* 打印订单处理结果摘要
*/
private static void printSummary(OrderProcessSummary summary) {
System.out.println("\n===== 批量订单处理结果汇总 =====");
System.out.println("总订单数: " + summary.getTotalOrders());
System.out.println("成功处理: " + summary.getSuccessCount());
System.out.println("失败处理: " + summary.getFailureCount());
System.out.println("总金额: " + summary.getTotalAmount());
System.out.println("\n详细结果:");
for (OrderResult result : summary.getResults()) {
System.out.println(result);
}
}
}
Future 和 FutureTask的区别?
1、Future 接口
Future 是一个接口,表示一个异步计算的结果,它提供了获取任务执行结果的方法。在执行异步任务时,你可以通过 Future 来控制任务的执行状态,并获取其结果。Future 主要有以下几个重要的方法:
- get(): 用于获取任务的结果,如果任务还未完成,调用 get() 会被阻塞,直到任务完成并返回结果。
- cancel(boolean mayInterruptIfRunning): 用于取消任务的执行,如果任务已经开始并且设置为可以中断,则会尝试中断任务。
- isDone(): 用于检查任务是否已经完成。
- isCancelled(): 用于检查任务是否已经被取消。
2、FutureTask 类
FutureTask 是 Future 接口的一个实现类,并且它实现了 Runnable 接口。因此,FutureTask 不仅能作为一个 Future 对象来获取结果,还可以作为 Runnable 被提交给执行器(如 ExecutorService)以进行执行。
FutureTask 可以用于包装一个任务,当任务完成时,可以通过 FutureTask 来获取结果。它同时具有可取消性,并支持任务执行状态的查询。
FutureTask 主要特性:
- 它实现了 Runnable,可以直接作为 Runnable 任务提交给 ExecutorService 执行。
- 它实现了 Future,可以用来获取任务的结果。
- 它允许任务的取消。
Future 是一个接口,用于表示异步任务的结果,它不能直接执行任务。
FutureTask 是 Future 接口的实现,它既是一个 Runnable,又是一个 Future,可以同时作为任务和结果的容器。
FutureTask 可以直接提交给 ExecutorService 执行,而 Future 只是用来获取执行结果。
4、基于线程池的方式
线程和数据库连接这些资源都是非常宝贵的资源。那么每次需要的时候创建,不需要的时候销
毁,是非常浪费资源的。那么我们就可以使用缓存的策略,也就是使用线程池。
Java 里面线程池的顶级接口是 Executor,但是严格意义上讲 Executor 并不是一个线程池,而
只是一个执行线程的工具。真正的线程池接口是 ExecutorService。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建固定大小的线程池,包含10个线程
ExecutorService threadPool = Executors.newFixedThreadPool(10);
// 提交20个任务给线程池
for (int i = 1; i <= 20; i++) {
final int taskId = i;
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 正在执行任务 " + taskId);
try {
// 模拟任务执行时间
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("任务 " + taskId + " 被中断");
}
System.out.println("任务 " + taskId + " 完成");
});
}
// 优雅关闭线程池
threadPool.shutdown();
try {
// 等待所有任务完成,最多等待1分钟
if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
// 如果超时仍有任务未完成,强制关闭
threadPool.shutdownNow();
}
} catch (InterruptedException e) {
threadPool.shutdownNow();
Thread.currentThread().interrupt();
}
System.out.println("所有任务已完成,线程池已关闭");
}
}
1、newCachedThreadPool(可缓存线程池)
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行
很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造
的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并
从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资
源。
特点:
- 线程数量几乎无限制(Integer.MAX_VALUE)
- 空闲线程默认存活60秒
- 使用同步队列(SynchronousQueue)
- 线程空闲时会被回收
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
2、newFixedThreadPool(固定大小线程池)
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大
多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,
则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何
线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之
前,池中的线程将一直存在。
特点:
- 固定线程数量
- 核心线程数 = 最大线程数 = nThreads
- 使用无界队列(LinkedBlockingQueue)
- 线程空闲时不会被回收
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads);
3、newScheduledThreadPool(定时任务线程池)
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
特点:
- 支持定时及周期性任务执行
- 核心线程数固定,非核心线程数无限制
- 空闲的非核心线程会被立即回收
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);
4、newSingleThreadExecutor(单线程线程池)
Executors.newSingleThreadExecutor()返回一个线程池(这个线程池只有一个线程),这个线程
池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去!
特点:
- 只有一个工作线程
- 保证任务按提交顺序执行
- 使用无界队列(LinkedBlockingQueue)
- 线程异常终止时会新建一个线程
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
注意:不推荐直接使用Executors创建线程池、建议通过ThreadPoolExecutor构造函数创建
ExecutorService customThreadPool = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(100) // 任务队列
);
5、ThreadPoolExecutor
ThreadPoolExecutor 是 Java 并发包 (java.util.concurrent) 中提供的线程池实现类,它是 Java 标准库中最强大、最灵活的线程池实现。
ThreadPoolExecutor 是一个可配置的线程池实现,它提供了比 Executors 工厂方法更精细的控制
public ThreadPoolExecutor(int corePoolSize, // 核心工作线程(当前任务执行结束后,不会被销毁)
int maximumPoolSize, // 最大工作线程(代表当前线程池中,一共可以有多少个工作线程)
long keepAliveTime, // 非核心工作线程在阻塞队列位置等待的时间
TimeUnit unit, // 非核心工作线程在阻塞队列位置等待时间的单位
BlockingQueue<Runnable> workQueue, // 任务在没有核心工作线程处理时,任务先扔到阻塞队列中
ThreadFactory threadFactory, // 构建线程的线程工作,可以设置thread的一些信息
RejectedExecutionHandler handler) { // 当线程池无法处理投递过来的任务时,执行当前的拒绝策略
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
Java 获取CPU核心数:
Runtime.getRuntime().availableProcessors()
Linux 获取CPU核心数:
[root@192 ~]# cat /proc/cpuinfo|grep "physical id"|sort|uniq|wc -l
1
[root@192 ~]# cat /proc/cpuinfo|grep "cpu cores"|uniq
cpu cores : 4
[root@192 ~]# cat /proc/cpuinfo|grep "processor"|wc -l
8
服务器配置分析:
- 物理CPU:1个
- CPU核心数:4核
- 逻辑处理器:8线程
推荐配置方案
1. CPU密集型任务(如视频转码、数学计算)
int physicalCores = 14;
ThreadPoolExecutor executor = new ThreadPoolExecutor(
physicalCores, // corePoolSize = 14
physicalCores, // maximumPoolSize = 14(不超物理核心数)
30, // keepAliveTime = 30s
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000), // 适度队列容量
new NamedThreadFactory("cpu-intensive-"),
new ThreadPoolExecutor.AbortPolicy()
);
2. I/O密集型任务(如数据库查询、网络请求)
int logicalCpus = 18;
ThreadPoolExecutor executor = new ThreadPoolExecutor(
14, // corePoolSize = 物理核心数
logicalCpus * 2, // maximumPoolSize = 36(逻辑CPU的2倍)
60, // keepAliveTime = 60s
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(2000), // 较大队列
new NamedThreadFactory("io-intensive-"),
new ThreadPoolExecutor.CallerRunsPolicy() // 降级策略
);
3. 通用型任务(混合负载)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
14, // 基础核心数
28, // 物理核心数的2倍
45, // 保持45秒
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(500), // 有界队列防OOM
new NamedThreadFactory("mixed-task-"),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
终止线程的方式
1、正常运行结束
程序运行结束,线程自动结束。
2、使用退出标志退出线程
一般 run()方法执行完,线程就会正常结束,然而,常常有些线程是伺服线程。它们需要长时间的运行,只有在外部某些条件满足的情况下,才能关闭这些线程。使用一个变量来控制循环,例如:最直接的方法就是设一个boolean 类型的标志,并通过设置这个标志为 true或 false 来控制 while循环是否退出
package com.taiyuan.javademoone.executorServicedemo;
/**
* 使用volatile修饰的布尔变量,实现线程安全终止
*/
public class FlagThread extends Thread {
// 定义一个volatile修饰的布尔变量,用于控制线程的运行状态
private volatile boolean running = true;
@Override
public void run() {
// 当running为true时,线程一直运行
while (running) {
// 执行任务
try {
// 暂停1秒
Thread.sleep(1000);
System.out.println("线程运行中...");
} catch (InterruptedException e) {
// 如果线程被中断,则设置当前线程的中断状态,并退出循环
Thread.currentThread().interrupt();
System.out.println("线程被中断");
break;
}
}
// 线程安全终止
System.out.println("线程安全终止");
}
// 停止线程的方法
public void stopThread() {
// 将running设置为false,线程退出循环
running = false;
}
public static void main(String[] args) {
// 使用示例
FlagThread thread = new FlagThread();
// 启动线程
thread.start();
try {
System.out.println("线程开始运行");
// 暂停5秒
Thread.sleep(5000);
} catch (InterruptedException e) {
// 抛出运行时异常
throw new RuntimeException(e);
}
// 停止线程
thread.stopThread();
}
}
3、使用interrupt()方法中断线程
使用 interrupt()方法来中断线程有两种情况:
1. 线程处于阻塞状态:如使用了 sleep,同步锁的 wait,socket 中的 receiver,accept 等方法时,会使线程处于阻塞状态。当调用线程的 interrupt()方法时,会抛出 InterruptException 异常。阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后 break 跳出循环状态,从而让我们有机会结束这个线程的执行。通常很多人认为只要调用 interrupt 方法线程就会结束,实际上是错的, 一定要先捕获 InterruptedException 异常之后通过 break 来跳出循环,才能正常结束 run 方法。
package com.taiyuan.javademoone.executorServicedemo;
public class InterruptThread extends Thread {
@Override
public void run() {
// 当线程没有被中断时,继续运行
while (!Thread.currentThread().isInterrupted()) {
try {
// 输出线程运行中
System.out.println("线程运行中...");
// 线程休眠1秒
Thread.sleep(2000);
} catch (InterruptedException e) {
// 捕获到中断异常,输出准备退出
System.out.println("捕获到中断异常,准备退出");
Thread.currentThread().interrupt(); // 重新设置中断标志
break;
}
}
System.out.println("线程已终止");
}
public static void main(String[] args) {
// 使用示例
InterruptThread thread = new InterruptThread();
thread.start();
try {
// 线程休眠5秒
Thread.sleep(5000);
} catch (InterruptedException e) {
// 抛出运行时异常
throw new RuntimeException(e);
}
// 中断线程
thread.interrupt();
}
}
2. 线程未处于阻塞状态:使用 isInterrupted()判断线程的中断标志来退出循环。当使用interrupt()方法时,中断标志就会置 true,和使用自定义的标志来控制循环是一样的道理。
package com.taiyuan.javademoone.executorServicedemo;
public class NonBlockingThread extends Thread {
@Override
public void run() {
// 使用 isInterrupted() 检查中断状态
while (!Thread.currentThread().isInterrupted()) {
// 模拟非阻塞任务
System.out.println("线程正在执行任务..."+Thread.currentThread().getName());
}
System.out.println("线程检测到中断请求,准备终止...");
System.out.println("执行清理工作...");
System.out.println("线程已安全终止");
}
public static void main(String[] args) throws InterruptedException {
NonBlockingThread thread = new NonBlockingThread();
thread.start();
// 主线程休眠3秒
Thread.sleep(1000);
// 中断工作线程
System.out.println(Thread.currentThread().getName()+"主线程请求中断工作线程");
// 调用 interrupt() 方法
// thread.interrupt(); 的作用是向 NonBlockingThread 线程发送中断请求,设置该线程的中断状态为 true。
// 如果目标线程正在阻塞(如在 sleep()、wait() 或 I/O 操作中),则会抛出 InterruptedException 并清除中断状态。
thread.interrupt();
// 等待工作线程真正结束 thread.join(); 的作用是让主线程等待 NonBlockingThread 线程执行完毕后再继续向下执行
thread.join();
System.out.println("主线程结束");
}
}
4、stop 方法终止线程 方法终止线程(线程不安全、不推荐使用)
程序中可以直接使用 thread.stop()来强行终止线程,但是 stop 方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,不安全主要是:thread.stop()调用之后,创建子线程的线程就会抛出 ThreadDeatherror 的错误,并且会释放子线程所持有的所有锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。因此,并不推荐使用 stop 方法来终止线程。
public class StopThread extends Thread {
@Override
public void run() {
try {
while (true) {
System.out.println("线程运行中...");
Thread.sleep(1000);
}
} catch (Throwable t) {
System.out.println("线程被强制终止");
}
}
public static void main(String[] args) throws InterruptedException {
// 使用示例
StopThread thread = new StopThread();
thread.start();
Thread.sleep(5000);
thread.stop(); // 已废弃的方法
}
}
5、线程池的关闭
何时需要关闭线程池?
线程池在以下几种情况下需要被关闭:
1.应用程序关闭或结束:当应用程序结束时,线程池应该被关闭,以释放系统资源。
2.不再需要处理新任务:如果应用程序达到所需功能,不再需要执行更多任务时,应关闭线程池,。
3.处理异常或错误状态:当出现异常情况导致线程池无法正常工作时,也应该定位并关闭线程池,。
Java的ExecutorService提供了三种关闭方法:
shutdown() - 平缓关闭,不再接受新任务,但会执行完已提交的任务
shutdownNow() - 立即关闭,尝试停止所有正在执行的任务,返回等待执行的任务列表
awaitTermination() - 阻塞等待线程池关闭完成
优雅关闭的最佳实践:
executor.shutdown(); // 拒绝新任务
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { // 等待现有任务完成
executor.shutdownNow(); // 取消正在执行的任务
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("线程池未完全关闭");
}
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
分步详解:
1. executor.shutdown()
- 作用:启动有序关闭过程
- 行为:
-
- 停止接受新任务提交
- 已提交的任务会继续执行
- 不会影响已经在队列中等待的任务
- 特点:这是"温和"的关闭方式,给正在运行的任务完成的机会
2. awaitTermination(60, TimeUnit.SECONDS)
- 作用:阻塞等待线程池完全终止
- 参数:
-
- 60:超时时间(可根据业务调整)
- TimeUnit.SECONDS:时间单位
- 返回值:
-
- true:所有任务已完成,线程池已关闭
- false:超时后仍有任务在执行
- 设计考虑:设置超时防止无限等待,避免程序卡死
3. executor.shutdownNow()
- 作用:尝试立即停止所有正在执行的任务
- 行为:
-
- 停止接受新任务
- 尝试中断正在执行的任务(通过Thread.interrupt())
- 返回等待执行的任务列表
- 注意:任务必须正确响应中断才能被停止
4. 第二次awaitTermination
- 目的:给被中断的任务一个响应中断的时间
- 必要性:即使调用shutdownNow(),任务停止也需要时间
- 业务考虑:这个超时可以比第一次短,因为主要是等待中断响应
5. 错误输出
- 触发条件:两次等待后线程池仍未关闭
- 处理建议:
-
- 记录更详细的日志(如剩余任务数)
- 可能需要人工干预或报警
- 考虑强制退出或重启
6. 中断处理(catch块)
- 触发条件:等待过程中当前线程被中断
- 处理逻辑:
-
- 立即尝试停止线程池(shutdownNow)
- 恢复中断状态(Thread.currentThread().interrupt())
- 重要性:保持中断状态是Java多线程最佳实践
案例:
package com.taiyuan.javademoone.executorServicedemo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* 如何优雅关闭线程池
*/
public class SimpleThreadPoolShutdown {
public static void main(String[] args) {
// 创建固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交10个任务
for (int i = 0; i < 20; i++) {
final int taskId = i;
executor.execute(() -> {
try {
System.out.println("执行任务 " + taskId + " 由 " + Thread.currentThread().getName());
Thread.sleep(1000); // 模拟任务执行时间
System.out.println("完成任务 " + taskId);
} catch (InterruptedException e) {
System.out.println("任务 " + taskId + " 被中断");
}
});
}
// 优雅关闭线程池
gracefulShutdown(executor, 1, TimeUnit.SECONDS);
}
/**
* 优雅关闭线程池
* @param executor 线程池
* @param timeout 等待超时时间
* @param unit 时间单位
* awaitTermination (long timeout, TimeUnit unit)
* 如果此执行程序终止,则为 true,如果终止前超时已过,则返回 false 抛出: InterruptedException – 如果在等待时中断
*/
public static void gracefulShutdown(ExecutorService executor, long timeout, TimeUnit unit) {
// 不再接受新任务
executor.shutdown();
try {
// 等待现有任务完成
if (!executor.awaitTermination(timeout, unit)) {
System.out.println("等待任务完成超时");
// 取消正在执行的任务
executor.shutdownNow();
// 再次等待任务响应取消
if (!executor.awaitTermination(timeout, unit)) {
System.err.println("线程池未完全关闭");
}
}
System.out.println("线程池已关闭");
} catch (InterruptedException e) {
System.out.println("InterruptedException 异常 等待任务完成被中断");
// 如果当前线程也被中断,再次尝试停止线程池
executor.shutdownNow();
// 保持中断状态
Thread.currentThread().interrupt();
}
}
}
模拟一个Web服务器处理请求的场景:
package com.taiyuan.javademoone.executorServicedemo;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Web服务器模拟 线程池
*/
public class WebServerSimulation {
// 模拟请求计数器 AtomicInteger是线程安全的整型计数器
private static final AtomicInteger requestCounter = new AtomicInteger(0);
public static void main(String[] args) {
// 创建线程池 - 模拟Web服务器的请求处理线程池
ThreadPoolExecutor serverExecutor = new ThreadPoolExecutor(
4, // 核心线程数
8, // 最大线程数
30, TimeUnit.SECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<>(100), // 任务队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 模拟请求不断到来
// 创建一个单线程的定时调度池来模拟请求发送者
ScheduledExecutorService requestSimulator = Executors.newScheduledThreadPool(1);
// 每200毫秒调度一次模拟请求任务
requestSimulator.scheduleAtFixedRate(() -> {
// 生成请求ID
int requestId = requestCounter.incrementAndGet();
// 提交处理请求的任务到服务器执行线程池
serverExecutor.execute(() -> handleRequest(requestId));
}, 0, 200, TimeUnit.MILLISECONDS); // 每200毫秒一个请求
// 模拟服务器运行一段时间后需要关闭
try {
Thread.sleep(5000); // 服务器运行5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("准备关闭服务器...");
// 先停止请求模拟器
requestSimulator.shutdownNow();
// 优雅关闭服务器线程池
gracefulShutdown(serverExecutor, 10, TimeUnit.SECONDS);
System.out.println("服务器已完全关闭");
}
/**
* 处理请求
*/
private static void handleRequest(int requestId) {
System.out.println("开始处理请求 " + requestId + " 由 " + Thread.currentThread().getName());
try {
// 模拟处理请求的时间 - 随机0.5-2秒
Thread.sleep(500 + (long) (Math.random() * 1500));
System.out.println("完成请求 " + requestId);
} catch (InterruptedException e) {
System.out.println("请求 " + requestId + " 处理被中断");
}
}
/**
* 优雅关闭线程池
*
* @param executor 线程池
* @param timeout 等待超时时间
* @param unit 时间单位
*/
public static void gracefulShutdown(ExecutorService executor, long timeout, TimeUnit unit) {
System.out.println("开始关闭线程池...");
System.out.println("当前活动线程数: " + ((ThreadPoolExecutor) executor).getActiveCount());
System.out.println("队列中等待任务数: " + ((ThreadPoolExecutor) executor).getQueue().size());
// 不再接受新任务
executor.shutdown();
try {
// 等待现有任务完成
if (!executor.awaitTermination(timeout, unit)) {
System.out.println("超时,尝试强制停止正在运行的任务...");
// 取消正在执行的任务
List<Runnable> droppedTasks = executor.shutdownNow();
System.out.println("被取消的任务数: " + droppedTasks.size());
// 再次等待任务响应取消
if (!executor.awaitTermination(timeout, unit)) {
System.err.println("线程池未完全关闭");
}
}
} catch (InterruptedException e) {
// 如果当前线程也被中断,再次尝试停止线程池
executor.shutdownNow();
// 保持中断状态
Thread.currentThread().interrupt();
}
}
}
sleep 与 与 wait 的区别?
1. 对于 sleep()方法,我们首先要知道该方法是属于 Thread 类中的。而 wait()方法,则是属于
Object 类中的。
2. sleep()方法导致了程序暂停执行指定的时间,让出 cpu 该其他线程,但是他的监控状态依然
保持者,当指定的时间到了又会自动恢复运行状态。
3. 在调用 sleep()方法的过程中,线程不会释放对象锁。
4. 而当调用 wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此
对象调用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
|
特性 |
|
|
|
所属类 |
Thread 类的静态方法 |
Object 类的实例方法 |
|
锁释放 |
不释放任何锁 |
释放对象锁 |
|
唤醒条件 |
时间到期或中断 |
notify()/notifyAll()或中断或超时 |
|
使用位置 |
任何代码上下文 |
必须在同步块/方法中(synchronized) |
|
异常处理 |
需要捕获InterruptedException |
需要捕获InterruptedException |
start 与 与 run 区别?
- start()方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕,
可以直接继续执行下面的代码。
- 通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运
行。
- 方法 run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,开始运
行 run 函数当中的代码。 Run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。
5万+

被折叠的 条评论
为什么被折叠?



