Java 多线程教程
一、多线程基础概念
1. 进程与线程的区别与关系
进程(Process)
进程是操作系统进行资源分配的基本单位,每个进程拥有:
- 独立的内存地址空间(代码段、数据段、堆栈等)
- 独立的文件描述符表(打开的文件、网络连接等)
- 独立的系统资源(如信号处理、环境变量等)
- 进程间切换涉及完整上下文保存(包括内存映射、寄存器状态等),开销较大(通常需要数千个CPU周期)
示例:在操作系统中,浏览器、文本编辑器和音乐播放器通常是不同的进程。
线程(Thread)
线程是进程内的执行单元,具有以下特点:
- 共享所属进程的所有资源(内存空间、打开的文件等)
- 拥有独立的程序计数器、寄存器和栈空间
- 线程切换仅需保存少量上下文信息(主要是寄存器状态),开销远小于进程(通常只需数百个CPU周期)
- 线程间通信可通过共享内存直接进行,无需系统调用
关系
- 一个进程可以包含多个线程(主流操作系统允许一个进程创建数百甚至数千个线程)
- 线程是CPU调度的基本单位,现代操作系统调度器直接调度线程而非进程
- 进程是资源分配的边界,线程是执行调度的单位
2. 多线程的优势与应用场景
提高资源利用率
- 在IO密集型应用中(如Web服务器、数据库系统),当线程因网络请求或文件IO阻塞时,CPU可立即切换到其他就绪线程
- 示例:Web服务器使用线程池处理并发请求,当一个线程等待数据库响应时,其他线程可以继续处理新请求
提升程序响应速度
- GUI程序:主线程负责界面渲染和事件响应,工作线程处理耗时操作(如文件加载、复杂计算)
- 示例:视频编辑软件中,界面保持流畅响应同时,后台线程进行视频编码和导出
并行处理能力
- 在多核CPU环境下,操作系统可将不同线程调度到不同核心真正并行执行
- 计算密集型应用:如图像处理、科学计算,可将任务分解由多个线程并行处理
- 示例:使用4个线程处理大型矩阵运算,在4核CPU上可获得接近4倍的性能提升
3. 线程的完整生命周期
Java线程的6种状态定义在Thread.State
枚举中,完整转换关系如下:
新建(New)
- 线程对象通过
new Thread()
创建后的初始状态 - 此时尚未调用
start()
方法,系统未分配资源
- 线程对象通过
运行(Runnable)
- 包含两种子状态:
- 就绪(Ready):线程已调用
start()
,等待线程调度器分配CPU时间 - 运行中(Running):线程获得CPU时间,正在执行
run()
方法
- 就绪(Ready):线程已调用
- 状态转换:
- 从New进入:调用
start()
方法 - 可能因时间片用完或
yield()
返回Ready状态
- 从New进入:调用
- 包含两种子状态:
阻塞(Blocked)
- 线程试图获取对象锁(同步代码块)而该锁被其他线程持有时
- 示例:
synchronized(lockObj) { // 如果锁被占用,线程进入Blocked状态// 临界区代码 }
- 当锁释放时,系统会选择一个阻塞线程转为Runnable
等待(Waiting)
- 线程通过以下方式进入无限期等待:
Object.wait()
(需配合synchronized使用)Thread.join()
(不带超时参数)LockSupport.park()
- 必须由其他线程通过
notify()
/notifyAll()
或中断来唤醒
- 线程通过以下方式进入无限期等待:
超时等待(Timed Waiting)
- 线程通过以下方式进入有限期等待:
Thread.sleep(long)
Object.wait(long)
Thread.join(long)
LockSupport.parkNanos()
- 时间到期后自动转换为Runnable状态
- 线程通过以下方式进入有限期等待:
终止(Terminated)
- 线程执行结束的最终状态:
run()
方法正常执行完毕- 遇到未捕获异常导致线程意外终止
- 终止后调用
start()
会抛出IllegalThreadStateException
- 线程执行结束的最终状态:
二、多线程的实现方式
Java 中实现多线程主要有三种方式:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口(结合 Future)。每种方式都有其特点和适用场景。
1. 继承 Thread 类
Thread 类是 Java 中线程操作的核心类,继承该类需要重写 run() 方法(线程执行体),通过调用 start() 方法启动线程。
public class MyThread extends Thread {@Overridepublic void run() {// 线程执行体for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + ":" + i);try {Thread.sleep(100); // 模拟耗时操作,睡眠100毫秒} catch (InterruptedException e) {e.printStackTrace(); // 处理中断异常}}}public static void main(String[] args) {// 创建线程实例MyThread thread1 = new MyThread();MyThread thread2 = new MyThread();// 设置线程名称thread1.setName("线程1");thread2.setName("线程2");// 启动线程(会调用run方法)thread1.start();thread2.start();}
}
注意事项:
线程启动必须调用 start() 方法而非直接调用 run() 方法:
- 直接调用 run() 会作为普通方法执行,不会创建新线程
- start() 方法会创建新的线程并自动调用 run() 方法
Java 单继承特性限制:
- 由于 Java 不支持多重继承,继承 Thread 类后将无法再继承其他类
- 这种方式的灵活性较低,不推荐在需要继承其他类的情况下使用
适用场景:
- 简单的线程任务
- 不需要共享资源的独立线程
- 不需要继承其他类的情况
2. 实现 Runnable 接口
Runnable 接口是 Java 提供的另一种创建线程的方式,它仅定义了一个 run() 方法。通过实现该接口可以避免单继承的限制,并且更适合多线程共享资源的场景。
public class MyRunnable implements Runnable {private int count = 5; // 多线程共享的资源@Overridepublic void run() {while (count > 0) {// 打印当前线程名和剩余计数System.out.println(Thread.currentThread().getName() + ":" + count);count--; // 操作共享资源try {Thread.sleep(100); // 模拟处理时间} catch (InterruptedException e) {e.printStackTrace(); // 处理中断异常}}}public static void main(String[] args) {// 创建Runnable实例MyRunnable runnable = new MyRunnable();// 多个线程共享同一个Runnable实例Thread thread1 = new Thread(runnable, "线程A");Thread thread2 = new Thread(runnable, "线程B");// 启动线程thread1.start();thread2.start();}
}
优势:
避免单继承限制:
- 由于 Java 可以同时实现多个接口,这种方式不会影响继承其他类
- 提高了代码的灵活性
资源共享:
- 多个线程可以共享同一个 Runnable 实例
- 适合需要共享资源的场景(如上述代码中的 count 变量)
线程池支持:
- 线程池通常只接受 Runnable 类型的任务
- 这种实现方式更容易与 Java 并发工具类集成
适用场景:
- 需要资源共享的多线程应用
- 需要继承其他类的情况
- 线程池任务
3. 实现 Callable 接口(结合 Future)
Callable 接口与 Runnable 类似,但提供了更强大的功能:支持返回结果且可以抛出异常。通常需要结合 Future 或 FutureTask 来获取执行结果。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class MyCallable implements Callable<Integer> {private int num; // 计算1到num的和public MyCallable(int num) {this.num = num; // 通过构造函数传入参数}@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 1; i <= num; i++) {sum += i; // 累加计算}return sum; // 返回计算结果}public static void main(String[] args) throws ExecutionException, InterruptedException {// 创建Callable实例MyCallable callable = new MyCallable(100);// 使用FutureTask包装CallableFutureTask<Integer> futureTask = new FutureTask<>(callable);// 创建线程并启动Thread thread = new Thread(futureTask, "计算线程");thread.start();// 获取线程执行结果(若未完成则阻塞等待)System.out.println("1到100的和为:" + futureTask.get());}
}
特点:
返回值支持:
- call() 方法可以返回结果,返回类型由泛型指定
- 相比 Runnable 的 run() 方法更灵活
异常处理:
- call() 方法可以抛出异常
- 调用者可以通过 Future.get() 捕获这些异常
结果获取:
- 通过 FutureTask.get() 获取结果(同步阻塞方法)
- 可以调用 FutureTask.isDone() 检查任务是否完成
适用场景:
- 需要获取线程执行结果的场景
- 需要处理线程中可能出现的异常
- 复杂的计算任务
- 需要异步获取结果的场景
比较三种方式:
- 继承 Thread 类:简单直接,但不灵活(单继承限制)
- 实现 Runnable 接口:灵活,支持资源共享,但无法返回结果
- 实现 Callable 接口:最强大,支持返回值和异常处理,但使用稍复杂
在实际开发中,推荐优先使用 Runnable 或 Callable 接口的方式,因为它们更灵活,更符合面向接口编程的原则,也更容易与 Java 并发工具类集成。
三、线程的常用方法
1. 线程控制方法
方法 | 说明 |
start() | 启动线程,将线程纳入 CPU 调度队列 |
run() | 线程执行体,包含线程要执行的逻辑 |
sleep(long millis) | 让当前线程休眠指定毫秒数,期间释放 CPU 但不释放锁 |
join() | 等待调用该方法的线程执行完毕(如threadA.join()表示当前线程等待 threadA 结束) |
yield() | 暂停当前线程,将 CPU 资源让给优先级相同或更高的线程(仅为建议,CPU 可能忽略) |
interrupt() | 中断线程(设置中断标志,需线程主动检测并处理) |
isInterrupted() | 判断线程是否被中断(不清除中断标志) |
interrupted() | 静态方法,判断当前线程是否被中断(清除中断标志) |
2.线程优先级
在 Java 并发编程中,线程优先级是一个重要的调度属性。Java 为线程定义了 10 个优先级级别,范围从 1(最低)到 10(最高),其中默认优先级为 5(Thread.NORM_PRIORITY)。开发者可以通过 Thread 类的 setPriority(int) 方法来调整线程优先级。
需要注意的是:
- 优先级设置必须在调用 start() 方法之前完成
- 不同操作系统对线程优先级的处理可能不同
- 高优先级线程获得 CPU 调度的概率更高,但不保证绝对优先执行
示例代码:
Thread thread1 = new Thread(() -> {System.out.println("高优先级线程正在运行");
});
thread1.setPriority(Thread.MAX_PRIORITY); // 设置为最高优先级10Thread thread2 = new Thread(() -> {System.out.println("低优先级线程正在运行");
});
thread2.setPriority(Thread.MIN_PRIORITY); // 设置为最低优先级1Thread thread3 = new Thread(() -> {System.out.println("默认优先级线程正在运行");
});
// 不设置优先级,默认为5// 启动线程
thread1.start();
thread2.start();
thread3.start();
应用场景:
- 实时任务处理(如视频渲染)可设为高优先级
- 后台日志记录可设为低优先级
- 普通的业务处理可使用默认优先级
注意事项:
- 过度依赖线程优先级可能导致线程饥饿问题
- 实际执行效果还取决于操作系统的线程调度策略
- 建议使用优先级范围在 1-10 之间,超出范围会抛出 IllegalArgumentException
四、线程同步机制(解决线程安全问题)
当多个线程并发访问共享资源时,可能出现数据不一致的线程安全问题。例如在之前的MyRunnable示例中,多个线程同时操作count变量可能导致数据竞态(race condition),表现为:
- 线程A读取count=5
- 线程B同时读取count=5
- 线程A和B都执行count--
- 最终count=4而非预期的3
1. synchronized关键字详解
synchronized是Java内置的线程同步机制,通过互斥锁(mutex)保证同一时间只有一个线程可以访问被保护的代码区域。
1.1 同步方法
// 实例方法同步
public synchronized void instanceMethod() {// 锁对象是当前实例(this)
}// 静态方法同步
public static synchronized void staticMethod() {// 锁对象是类的Class对象(如MyClass.class)
}
应用场景:
- 当整个方法都需要同步保护时
- 适合简单的同步需求,如计数器操作
1.2 同步代码块
// 使用专用锁对象
private final Object lock = new Object();public void method() {// 非同步代码...synchronized(lock) {// 临界区代码}// 非同步代码...
}
最佳实践:
- 锁对象应声明为private final,防止外部修改
- 同步块应尽量小,减少性能影响
- 避免使用String或基础类型包装类作为锁
1.3 改进的SafeRunnable实现
public class SafeRunnable implements Runnable {private int count = 5;private final Object lock = new Object(); // 专用锁对象@Overridepublic void run() {while (true) {synchronized (lock) {if (count <= 0) break;System.out.printf("%s:%d%n", Thread.currentThread().getName(), count);count--;}try {Thread.sleep(100); // 模拟耗时操作} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}}
}
2. ReentrantLock高级锁机制
2.1 基本用法
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockExample implements Runnable {private int count = 5;private final Lock lock = new ReentrantLock();@Overridepublic void run() {while (true) {lock.lock(); // 获取锁try {if (count <= 0) break;System.out.printf("%s:%d%n",Thread.currentThread().getName(), count);count--;} finally {lock.unlock(); // 必须在finally中释放锁}try {Thread.sleep(100);} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}}
}
2.2 高级特性
1. 尝试获取锁
if (lock.tryLock(1, TimeUnit.SECONDS)) {try {// 操作共享资源} finally {lock.unlock();}
} else {// 获取锁失败的处理
}
2. 公平锁与非公平锁
// 公平锁(按等待顺序获取锁)
Lock fairLock = new ReentrantLock(true);// 非公平锁(默认)
Lock unfairLock = new ReentrantLock();
3. 锁中断
lock.lockInterruptibly(); // 可响应中断的获取锁方式
try {// ...
} finally {lock.unlock();
}
2.3 synchronized与ReentrantLock对比
特性 | synchronized | ReentrantLock |
---|---|---|
锁获取方式 | JVM自动管理 | 手动lock/unlock |
可中断性 | 不支持 | 支持 |
超时尝试 | 不支持 | 支持tryLock |
公平锁 | 非公平 | 可配置 |
条件变量 | 有限 | 支持多个 |
性能 | 优化较好 | JDK5+优化 |
3. 线程间通信机制
3.1 生产者-消费者模型完整实现
public class ProductQueue {private final LinkedList<Integer> products = new LinkedList<>();private final int MAX_CAPACITY = 5;private final Object lock = new Object();public void produce(int item) throws InterruptedException {synchronized (lock) {while (products.size() == MAX_CAPACITY) {System.out.println("队列已满,生产者等待...");lock.wait();}products.add(item);System.out.printf("生产者[%s]生产:%d,当前数量:%d%n",Thread.currentThread().getName(), item, products.size());lock.notifyAll();}}public int consume() throws InterruptedException {synchronized (lock) {while (products.isEmpty()) {System.out.println("队列已空,消费者等待...");lock.wait();}int item = products.removeFirst();System.out.printf("消费者[%s]消费:%d,当前数量:%d%n",Thread.currentThread().getName(), item, products.size());lock.notifyAll();return item;}}
}
3.2 使用Condition实现精确通知
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class ImprovedQueue {private final ReentrantLock lock = new ReentrantLock();private final Condition notFull = lock.newCondition();private final Condition notEmpty = lock.newCondition();// ...其他成员变量public void produce(int item) throws InterruptedException {lock.lock();try {while (products.size() == MAX_CAPACITY) {notFull.await();}products.add(item);notEmpty.signal(); // 只唤醒消费者} finally {lock.unlock();}}public int consume() throws InterruptedException {lock.lock();try {while (products.isEmpty()) {notEmpty.await();}int item = products.removeFirst();notFull.signal(); // 只唤醒生产者return item;} finally {lock.unlock();}}
}
3.3 注意事项
- 必须在synchronized块或lock保护下调用wait/notify
- 使用while循环检查条件,避免虚假唤醒
- notifyAll()比notify()更安全,但性能稍差
- wait()会释放锁,sleep()不会释放锁
- 优先使用java.util.concurrent包中的高级同步工具
五、线程池(Executor 框架)
在 Java 并发编程中,频繁创建和销毁线程会消耗大量系统资源,不仅增加 JVM 内存开销,还会导致频繁的上下文切换,影响系统性能。线程池通过线程复用机制,可以有效解决这些问题,是 Java 并发编程的推荐实践。
1. 线程池核心参数(ThreadPoolExecutor)
线程池的核心实现类是 ThreadPoolExecutor
,其完整构造函数如下:
public ThreadPoolExecutor(int corePoolSize, // 核心线程数(始终保持的最小线程数)int maximumPoolSize, // 最大线程数(包括核心线程和非核心线程)long keepAliveTime, // 非核心线程空闲超时时间(超过则被回收)TimeUnit unit, // 超时时间单位(毫秒/秒/分钟等)BlockingQueue<Runnable> workQueue, // 任务等待队列ThreadFactory threadFactory, // 线程工厂(用于自定义线程创建)RejectedExecutionHandler handler // 拒绝策略(当队列和线程池都满时)
)
参数详细说明:
corePoolSize
:线程池中始终保持的线程数量,即使这些线程处于空闲状态maximumPoolSize
:线程池允许的最大线程数,当工作队列满时会创建新线程直到达到此限制keepAliveTime
:非核心线程在空闲状态下的存活时间,超过该时间会被回收workQueue
:常用的阻塞队列包括:ArrayBlockingQueue
:有界队列LinkedBlockingQueue
:无界队列(默认大小 Integer.MAX_VALUE)SynchronousQueue
:不存储元素的队列
handler
:拒绝策略,常见有:AbortPolicy
:直接抛出异常(默认)CallerRunsPolicy
:由调用者线程执行任务DiscardPolicy
:直接丢弃任务DiscardOldestPolicy
:丢弃队列中最老的任务
2. 常见线程池(Executors 工具类)
Java 提供了 Executors
工具类来创建常用的线程池:
固定大小线程池:
Executors.newFixedThreadPool(n)
- 特点:核心线程数 = 最大线程数,使用无界队列(LinkedBlockingQueue)
- 适用场景:适合处理长期稳定的并发任务
缓存线程池:
Executors.newCachedThreadPool()
- 特点:核心线程数为0,最大线程数为Integer.MAX_VALUE,使用SynchronousQueue
- 适用场景:适合大量短期异步任务
- 风险:在高并发下可能创建过多线程导致OOM
单线程池:
Executors.newSingleThreadExecutor()
- 特点:只有一个工作线程,保证任务按FIFO顺序执行
- 适用场景:需要顺序执行的任务
定时任务线程池:
Executors.newScheduledThreadPool(n)
- 特点:支持延迟或周期性执行任务
- 适用场景:定时任务、心跳检测等
3. 线程池使用示例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {// 创建固定大小为3的线程池ExecutorService executor = Executors.newFixedThreadPool(3);// 提交5个任务for (int i = 0; i < 5; i++) {final int taskId = i;executor.submit(() -> { // 使用lambda表达式提交任务System.out.println("任务" + taskId + "由" + Thread.currentThread().getName() + "执行");try {Thread.sleep(1000); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池(不再接受新任务,等待现有任务完成)executor.shutdown(); }
}
执行结果分析: 由于线程池大小为3,而任务有5个,输出会显示3个线程交替执行5个任务,证明了线程复用机制。
4. 线程池最佳实践
资源限制:
- 根据CPU核心数设置合理的线程数(通常CPU密集型任务设置为N+1,IO密集型设置为2N)
- 避免使用无界队列,防止内存溢出
线程池关闭:
shutdown()
:平缓关闭,等待已提交任务完成shutdownNow()
:立即关闭,尝试中断正在执行的任务
监控与调优:
- 监控线程池状态(活动线程数、队列大小等)
- 根据业务特点调整核心参数
注意:根据阿里Java开发手册,不推荐直接使用Executors
创建线程池,建议直接使用ThreadPoolExecutor
构造函数,这样可以更精确地控制所有参数,避免资源耗尽的风险(如newCachedThreadPool
可能创建过多线程导致OOM)。
六、多线程注意事项
1. 避免线程安全问题
1.1 同步机制的正确使用
共享资源必须通过同步机制保护,Java提供了两种主要方式:
- synchronized关键字:可以修饰方法或代码块
// 同步方法 public synchronized void increment() {counter++; }// 同步代码块 public void increment() {synchronized(this) {counter++;} }
- Lock接口:更灵活的锁机制
private final Lock lock = new ReentrantLock();public void increment() {lock.lock();try {counter++;} finally {lock.unlock();} }
1.2 不可变对象的使用
不可变对象(Immutable Objects)是线程安全的,因为它们的内部状态不能被修改:
- Java中的String、Integer、BigDecimal等都是不可变对象
- 创建自定义不可变类的原则:
- 类声明为final
- 所有字段设为final
- 不提供修改内部状态的方法
- 防止this引用逃逸
1.3 静态变量的使用注意事项
static变量是类级别的共享资源,容易引发线程安全问题:
- 示例问题:
public class Counter {private static int count = 0;public static void increment() {count++; // 非原子操作} }
- 解决方案:
- 使用synchronized修饰静态方法
- 使用AtomicInteger等原子类
- 避免不必要的静态变量
2. 防止死锁
2.1 死锁的四个必要条件
- 互斥条件
- 占有并等待
- 非抢占条件
- 循环等待条件
2.2 死锁预防策略
2.2.1 固定锁获取顺序
// 正确做法:所有线程都按相同顺序获取锁
public void transfer(Account from, Account to, int amount) {Account first = from.id < to.id ? from : to;Account second = from.id < to.id ? to : from;synchronized(first) {synchronized(second) {// 转账操作}}
}
2.2.2 使用tryLock超时
Lock lock1 = new ReentrantLock();
Lock lock2 = new ReentrantLock();public boolean transferWithTimeout() throws InterruptedException {if (lock1.tryLock(1, TimeUnit.SECONDS)) {try {if (lock2.tryLock(1, TimeUnit.SECONDS)) {try {// 执行转账操作return true;} finally {lock2.unlock();}}} finally {lock1.unlock();}}return false;
}
2.2.3 减少锁粒度
- 缩小同步代码块的范围
- 使用读写锁(ReadWriteLock)替代独占锁
- 考虑使用并发集合类
3. 线程中断的正确处理
3.1 线程中断机制
Java线程中断是一种协作机制:
interrupt()
:设置线程的中断标志isInterrupted()
:检查中断标志interrupted()
:检查并清除中断标志
3.2 中断处理的最佳实践
public class InterruptExample implements Runnable {@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()) {try {// 执行任务System.out.println("线程运行中...");Thread.sleep(1000); // 可响应中断的阻塞方法} catch (InterruptedException e) {// 恢复中断状态Thread.currentThread().interrupt();// 执行清理工作System.out.println("正在清理资源...");break;}}System.out.println("线程已中断");}public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new InterruptExample());thread.start();Thread.sleep(3000);thread.interrupt();}
}
3.3 不可中断阻塞的处理
对于不能响应中断的阻塞操作,可以:
- 关闭底层资源(如Socket.close())
- 使用Future和超时机制
- 考虑使用NIO的非阻塞I/O
4. 线程池的合理配置
4.1 线程池参数配置
任务类型 | 核心线程数公式 | 示例(4核CPU) |
---|---|---|
CPU密集型 | CPU核心数 + 1 | 5 |
IO密集型 | CPU核心数 × 2 | 8 |
混合型 | (CPU核心数/(1-阻塞系数)) × CPU核心数 | 需要计算 |
阻塞系数 = 阻塞时间/(阻塞时间+计算时间)
4.2 任务队列选择
- LinkedBlockingQueue:无界队列,可能导致OOM
- ArrayBlockingQueue:有界队列,需要合理设置大小
- SynchronousQueue:直接传递任务,适合高吞吐场景
- PriorityBlockingQueue:带优先级的无界队列
4.3 拒绝策略
- AbortPolicy:默认策略,直接抛出RejectedExecutionException
- CallerRunsPolicy:由调用线程执行该任务
- DiscardPolicy:直接丢弃任务
- DiscardOldestPolicy:丢弃队列中最老的任务
4.4 线程池创建示例
// CPU密集型任务
ExecutorService cpuPool = new ThreadPoolExecutor(5, // corePoolSize5, // maximumPoolSize0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(1000),new ThreadPoolExecutor.AbortPolicy()
);// IO密集型任务
ExecutorService ioPool = new ThreadPoolExecutor(8, // corePoolSize16, // maximumPoolSize60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),new ThreadPoolExecutor.CallerRunsPolicy()
);
4.5 监控线程池
可以通过以下方式监控线程池状态:
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);// 获取活动线程数
int activeCount = executor.getActiveCount();// 获取已完成任务数
long completedTaskCount = executor.getCompletedTaskCount();// 获取队列中的任务数
int queueSize = executor.getQueue().size();