在处理大量线程(例如 1000 个线程)同时运行时,防止系统卡顿和资源耗尽是非常重要的。以下是一些关键策略和最佳实践,帮助你有效地管理大量线程,确保系统的稳定性和性能。

1. 使用线程池

线程池是管理大量线程的最佳实践之一。线程池可以复用线程,减少线程创建和销毁的开销,同时控制并发线程的数量,避免系统资源耗尽。

优点
  • 资源管理:控制并发线程的数量,避免资源耗尽。
  • 性能优化:减少线程创建和销毁的开销。
  • 可配置性:可以根据需求调整线程池的参数(如核心线程数、最大线程数、任务队列大小等)。
示例代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExample {
    public static void main(String[] args) {
        int numberOfThreads = 1000;
        ExecutorService executorService = Executors.newFixedThreadPool(100); // 根据系统资源调整线程数

        for (int i = 0; i < numberOfThreads; i++) {
            final int taskNumber = i;
            executorService.submit(() -> {
                System.out.println("Task " + taskNumber + " is running on thread " + Thread.currentThread().getName());
                try {
                    TimeUnit.SECONDS.sleep(1); // 模拟任务执行时间
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executorService.shutdown();
        try {
            executorService.awaitTermination(1, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

2. 控制并发线程数

根据系统的资源(如 CPU 核心数、内存等)合理设置线程池的大小。过多的线程会导致上下文切换频繁,增加 CPU 负载,降低性能。

建议
  • CPU 密集型任务:线程数设置为 CPU 核心数。
  • I/O 密集型任务:线程数可以设置为 CPU 核心数的倍数(如 2 倍或 4 倍)。
示例
int numberOfCores = Runtime.getRuntime().availableProcessors();
ExecutorService executorService = Executors.newFixedThreadPool(numberOfCores * 2);

3. 使用合适的任务队列

选择合适的任务队列可以有效管理待执行的任务,避免任务堆积导致系统卡顿。

常见任务队列
  • LinkedBlockingQueue:无界队列,适用于任务量可预测且不会过多的情况。
  • ArrayBlockingQueue:有界队列,适用于任务量较大且需要限制队列大小的情况。
  • SynchronousQueue:不存储元素的阻塞队列,适用于生产者和消费者数量匹配的情况。
示例
ExecutorService executorService = Executors.newFixedThreadPool(100, new ArrayBlockingQueue<>(1000));

4. 优化任务逻辑

确保每个任务的逻辑高效,避免长时间占用线程。

建议
  • 避免长时间阻塞:尽量减少 I/O 操作和长时间的计算。
  • 异步处理:对于 I/O 操作,可以使用异步编程模型(如 CompletableFuture)。
  • 批量处理:对于批量任务,可以分批处理以减少单个任务的执行时间。
示例
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AsyncExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(100);

        for (int i = 0; i < 1000; i++) {
            final int taskNumber = i;
            CompletableFuture.runAsync(() -> {
                System.out.println("Task " + taskNumber + " is running on thread " + Thread.currentThread().getName());
                try {
                    Thread.sleep(100); // 模拟任务执行时间
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }, executorService);
        }

        executorService.shutdown();
        try {
            executorService.awaitTermination(1, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

5. 监控和调优

持续监控系统的性能和线程状态,根据监控数据进行调优。

常用监控工具
  • JConsole:Java 自带的监控工具,可以监控线程、内存、垃圾回收等。
  • VisualVM:功能更强大的监控工具,支持线程分析、内存分析等。
  • Prometheus + Grafana:用于监控和可视化系统指标。
监控指标
  • 线程数:监控当前活动的线程数。
  • CPU 使用率:监控 CPU 的使用情况。
  • 内存使用率:监控堆内存和非堆内存的使用情况。
  • 任务队列长度:监控任务队列的长度,避免任务堆积。

6. 处理拒绝策略

当线程池无法接受新任务时,需要定义合适的拒绝策略。

常见拒绝策略
  • AbortPolicy:默认策略,直接抛出 RejectedExecutionException
  • CallerRunsPolicy:由提交任务的线程执行任务。
  • DiscardPolicy:直接丢弃任务,不抛出异常。
  • DiscardOldestPolicy:丢弃队列中最老的任务,然后尝试重新提交新任务。
示例
import java.util.concurrent.*;

public class RejectionPolicyExample {
    public static void main(String[] args) {
        int corePoolSize = 10;
        int maximumPoolSize = 100;
        long keepAliveTime = 10;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100);
        RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();

        ExecutorService executorService = new ThreadPoolExecutor(
            corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);

        for (int i = 0; i < 1000; i++) {
            final int taskNumber = i;
            executorService.submit(() -> {
                System.out.println("Task " + taskNumber + " is running on thread " + Thread.currentThread().getName());
                try {
                    Thread.sleep(100); // 模拟任务执行时间
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executorService.shutdown();
        try {
            executorService.awaitTermination(1, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

7. 使用合适的锁机制

确保线程安全的同时,避免锁竞争导致的性能问题。

建议
  • 细粒度锁:使用细粒度锁减少锁竞争。
  • 读写锁:对于读多写少的场景,使用 ReentrantReadWriteLock 提高并发性能。
  • 原子变量:使用 java.util.concurrent.atomic 包中的原子变量(如 AtomicIntegerAtomicReference)减少锁的使用。
示例
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockExample {
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final Lock readLock = rwLock.readLock();
    private final Lock writeLock = rwLock.writeLock();
    private int count = 0;

    public void increment() {
        writeLock.lock();
        try {
            count++;
        } finally {
            writeLock.unlock();
        }
    }

    public int getCount() {
        readLock.lock();
        try {
            return count;
        } finally {
            readLock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReadWriteLockExample example = new ReadWriteLockExample();
        ExecutorService executorService = Executors.newFixedThreadPool(100);

        for (int i = 0; i < 1000; i++) {
            final int taskNumber = i;
            executorService.submit(() -> {
                example.increment();
                System.out.println("Task " + taskNumber + " incremented count to " + example.getCount());
            });
        }

        executorService.shutdown();
        try {
            executorService.awaitTermination(1, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

8. 避免死锁

确保线程在获取多个锁时遵循一致的顺序,避免死锁。

建议
  • 锁顺序:所有线程以相同的顺序获取锁。
  • 超时机制:使用 tryLock 方法并设置超时时间,避免无限期等待。
示例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class DeadlockAvoidanceExample {
    private final Lock lock1 = new ReentrantLock();
    private final Lock lock2 = new ReentrantLock();

    public void method1() {
        boolean lock1Acquired = false;
        boolean lock2Acquired = false;
        try {
            while (true) {
                try {
                    lock1Acquired = lock1.tryLock(10, TimeUnit.MILLISECONDS);
                    lock2Acquired = lock2.tryLock(10, TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }

                if (lock1Acquired && lock2Acquired) {
                    break;
                }

                if (lock1Acquired) {
                    lock1.unlock();
                    lock1Acquired = false;
                }

                if (lock2Acquired) {
                    lock2.unlock();
                    lock2Acquired = false;
                }
            }

            // 安全地执行操作
            System.out.println("Method 1: Locks acquired by " + Thread.currentThread().getName());
        } finally {
            if (lock1Acquired) {
                lock1.unlock();
            }
            if (lock2Acquired) {
                lock2.unlock();
            }
        }
    }

    public void method2() {
        boolean lock1Acquired = false;
        boolean lock2Acquired = false;
        try {
            while (true) {
                try {
                    lock1Acquired = lock1.tryLock(10, TimeUnit.MILLISECONDS);
                    lock2Acquired = lock2.tryLock(10, TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }

                if (lock1Acquired && lock2Acquired) {
                    break;
                }

                if (lock1Acquired) {
                    lock1.unlock();
                    lock1Acquired = false;
                }

                if (lock2Acquired) {
                    lock2.unlock();
                    lock2Acquired = false;
                }
            }

            // 安全地执行操作
            System.out.println("Method 2: Locks acquired by " + Thread.currentThread().getName());
        } finally {
            if (lock1Acquired) {
                lock1.unlock();
            }
            if (lock2Acquired) {
                lock2.unlock();
            }
        }
    }