当前位置: 首页 > ai >正文

Java 多线程教程

一、多线程基础概念

1. 进程与线程的区别与关系

进程(Process)

进程是操作系统进行资源分配的基本单位,每个进程拥有:

  • 独立的内存地址空间(代码段、数据段、堆栈等)
  • 独立的文件描述符表(打开的文件、网络连接等)
  • 独立的系统资源(如信号处理、环境变量等)
  • 进程间切换涉及完整上下文保存(包括内存映射、寄存器状态等),开销较大(通常需要数千个CPU周期)

示例:在操作系统中,浏览器、文本编辑器和音乐播放器通常是不同的进程。

线程(Thread)

线程是进程内的执行单元,具有以下特点:

  • 共享所属进程的所有资源(内存空间、打开的文件等)
  • 拥有独立的程序计数器、寄存器和栈空间
  • 线程切换仅需保存少量上下文信息(主要是寄存器状态),开销远小于进程(通常只需数百个CPU周期)
  • 线程间通信可通过共享内存直接进行,无需系统调用

关系

  • 一个进程可以包含多个线程(主流操作系统允许一个进程创建数百甚至数千个线程)
  • 线程是CPU调度的基本单位,现代操作系统调度器直接调度线程而非进程
  • 进程是资源分配的边界,线程是执行调度的单位

2. 多线程的优势与应用场景

提高资源利用率

  • 在IO密集型应用中(如Web服务器、数据库系统),当线程因网络请求或文件IO阻塞时,CPU可立即切换到其他就绪线程
  • 示例:Web服务器使用线程池处理并发请求,当一个线程等待数据库响应时,其他线程可以继续处理新请求

提升程序响应速度

  • GUI程序:主线程负责界面渲染和事件响应,工作线程处理耗时操作(如文件加载、复杂计算)
  • 示例:视频编辑软件中,界面保持流畅响应同时,后台线程进行视频编码和导出

并行处理能力

  • 在多核CPU环境下,操作系统可将不同线程调度到不同核心真正并行执行
  • 计算密集型应用:如图像处理、科学计算,可将任务分解由多个线程并行处理
  • 示例:使用4个线程处理大型矩阵运算,在4核CPU上可获得接近4倍的性能提升

3. 线程的完整生命周期

Java线程的6种状态定义在Thread.State枚举中,完整转换关系如下:

  1. 新建(New)

    • 线程对象通过new Thread()创建后的初始状态
    • 此时尚未调用start()方法,系统未分配资源
  2. 运行(Runnable)

    • 包含两种子状态:
      • 就绪(Ready):线程已调用start(),等待线程调度器分配CPU时间
      • 运行中(Running):线程获得CPU时间,正在执行run()方法
    • 状态转换:
      • 从New进入:调用start()方法
      • 可能因时间片用完或yield()返回Ready状态
  3. 阻塞(Blocked)

    • 线程试图获取对象锁(同步代码块)而该锁被其他线程持有时
    • 示例:
      synchronized(lockObj) { // 如果锁被占用,线程进入Blocked状态// 临界区代码
      }
      

    • 当锁释放时,系统会选择一个阻塞线程转为Runnable
  4. 等待(Waiting)

    • 线程通过以下方式进入无限期等待:
      • Object.wait()(需配合synchronized使用)
      • Thread.join()(不带超时参数)
      • LockSupport.park()
    • 必须由其他线程通过notify()/notifyAll()或中断来唤醒
  5. 超时等待(Timed Waiting)

    • 线程通过以下方式进入有限期等待:
      • Thread.sleep(long)
      • Object.wait(long)
      • Thread.join(long)
      • LockSupport.parkNanos()
    • 时间到期后自动转换为Runnable状态
  6. 终止(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();}
}

注意事项:

  1. 线程启动必须调用 start() 方法而非直接调用 run() 方法:

    • 直接调用 run() 会作为普通方法执行,不会创建新线程
    • start() 方法会创建新的线程并自动调用 run() 方法
  2. Java 单继承特性限制:

    • 由于 Java 不支持多重继承,继承 Thread 类后将无法再继承其他类
    • 这种方式的灵活性较低,不推荐在需要继承其他类的情况下使用
  3. 适用场景:

    • 简单的线程任务
    • 不需要共享资源的独立线程
    • 不需要继承其他类的情况

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();}
}

优势:

  1. 避免单继承限制:

    • 由于 Java 可以同时实现多个接口,这种方式不会影响继承其他类
    • 提高了代码的灵活性
  2. 资源共享:

    • 多个线程可以共享同一个 Runnable 实例
    • 适合需要共享资源的场景(如上述代码中的 count 变量)
  3. 线程池支持:

    • 线程池通常只接受 Runnable 类型的任务
    • 这种实现方式更容易与 Java 并发工具类集成
  4. 适用场景:

    • 需要资源共享的多线程应用
    • 需要继承其他类的情况
    • 线程池任务

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());}
}

特点:

  1. 返回值支持:

    • call() 方法可以返回结果,返回类型由泛型指定
    • 相比 Runnable 的 run() 方法更灵活
  2. 异常处理:

    • call() 方法可以抛出异常
    • 调用者可以通过 Future.get() 捕获这些异常
  3. 结果获取:

    • 通过 FutureTask.get() 获取结果(同步阻塞方法)
    • 可以调用 FutureTask.isDone() 检查任务是否完成
  4. 适用场景:

    • 需要获取线程执行结果的场景
    • 需要处理线程中可能出现的异常
    • 复杂的计算任务
    • 需要异步获取结果的场景

比较三种方式:

  1. 继承 Thread 类:简单直接,但不灵活(单继承限制)
  2. 实现 Runnable 接口:灵活,支持资源共享,但无法返回结果
  3. 实现 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) 方法来调整线程优先级。

需要注意的是:

  1. 优先级设置必须在调用 start() 方法之前完成
  2. 不同操作系统对线程优先级的处理可能不同
  3. 高优先级线程获得 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) {// 临界区代码}// 非同步代码...
}

最佳实践

  1. 锁对象应声明为private final,防止外部修改
  2. 同步块应尽量小,减少性能影响
  3. 避免使用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对比

特性synchronizedReentrantLock
锁获取方式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 注意事项

  1. 必须在synchronized块或lock保护下调用wait/notify
  2. 使用while循环检查条件,避免虚假唤醒
  3. notifyAll()比notify()更安全,但性能稍差
  4. wait()会释放锁,sleep()不会释放锁
  5. 优先使用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 工具类来创建常用的线程池:

  1. 固定大小线程池Executors.newFixedThreadPool(n)

    • 特点:核心线程数 = 最大线程数,使用无界队列(LinkedBlockingQueue)
    • 适用场景:适合处理长期稳定的并发任务
  2. 缓存线程池Executors.newCachedThreadPool()

    • 特点:核心线程数为0,最大线程数为Integer.MAX_VALUE,使用SynchronousQueue
    • 适用场景:适合大量短期异步任务
    • 风险:在高并发下可能创建过多线程导致OOM
  3. 单线程池Executors.newSingleThreadExecutor()

    • 特点:只有一个工作线程,保证任务按FIFO顺序执行
    • 适用场景:需要顺序执行的任务
  4. 定时任务线程池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. 线程池最佳实践

  1. 资源限制

    • 根据CPU核心数设置合理的线程数(通常CPU密集型任务设置为N+1,IO密集型设置为2N)
    • 避免使用无界队列,防止内存溢出
  2. 线程池关闭

    • shutdown():平缓关闭,等待已提交任务完成
    • shutdownNow():立即关闭,尝试中断正在执行的任务
  3. 监控与调优

    • 监控线程池状态(活动线程数、队列大小等)
    • 根据业务特点调整核心参数

注意:根据阿里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 死锁的四个必要条件

  1. 互斥条件
  2. 占有并等待
  3. 非抢占条件
  4. 循环等待条件

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 不可中断阻塞的处理

对于不能响应中断的阻塞操作,可以:

  1. 关闭底层资源(如Socket.close())
  2. 使用Future和超时机制
  3. 考虑使用NIO的非阻塞I/O

4. 线程池的合理配置

4.1 线程池参数配置

任务类型核心线程数公式示例(4核CPU)
CPU密集型CPU核心数 + 15
IO密集型CPU核心数 × 28
混合型(CPU核心数/(1-阻塞系数)) × CPU核心数需要计算

阻塞系数 = 阻塞时间/(阻塞时间+计算时间)

4.2 任务队列选择

  1. LinkedBlockingQueue:无界队列,可能导致OOM
  2. ArrayBlockingQueue:有界队列,需要合理设置大小
  3. SynchronousQueue:直接传递任务,适合高吞吐场景
  4. PriorityBlockingQueue:带优先级的无界队列

4.3 拒绝策略

  1. AbortPolicy:默认策略,直接抛出RejectedExecutionException
  2. CallerRunsPolicy:由调用线程执行该任务
  3. DiscardPolicy:直接丢弃任务
  4. 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();

http://www.xdnf.cn/news/17896.html

相关文章:

  • 心路历程-三个了解敲开linux的大门
  • 第三十七天(js前端数据加密和混淆)
  • 设计模式之静态代理
  • 拒绝造轮子(C#篇)使用SqlSugar实现数据库的访问
  • KingbaseES高可用架构深度解析——从读写分离到异地灾备的全方位守护
  • Vue2.x核心技术与实战(一)
  • Flutter InheritedWidget 详解:从生命周期到数据流动的完整解析
  • 《探索IndexedDB实现浏览器端UTXO模型的前沿技术》
  • Blackwell 和 Hopper 架构的 GPGPU 新功能全面综述
  • debian 13 显示中文字体 不再显示菱形块 终端显示中文
  • 【121页PPT】锂膜产业MESERP方案规划建议(附下载方式)
  • week1-[循环嵌套]画正方形
  • hex文件结构速查
  • Java研学-SpringCloud(三)
  • LCR 076. 数组中的第 K 个最大元素
  • 集成电路学习:什么是Image Segmentation图像分割
  • QT|windwos桌面端应用程序开发,当连接多个显示器的时候,如何获取屏幕编号?
  • 嵌入式第二十九课!!!回收子进程资源空间函数与exec函数
  • Deepoc具身智能模型如何重塑康复辅助设备
  • 如何理解AP中SM中宿主进程?
  • 另类的pdb恢复方式
  • 第十四节:物理引擎集成:Cannon.js入门
  • python自学笔记9 Seaborn可视化
  • 【100页PPT】数字化转型集团信息化总体解决方案(附下载方式)
  • 【机器人-基础知识】ROS1和ROS2对比
  • shell脚本实现sha256sum校验并拷贝校验通过的文件
  • OpenCV Python——图像查找(特征匹配 + 单应性矩阵)
  • Kotlin作用域函数全解:run/with/apply/let/also与this/it的魔法对决
  • fs模块_写入文件
  • Layers(图层)