前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程池合理使用规范

线程池合理使用规范

原创
作者头像
星辰大海的精灵
发布2024-08-06 11:25:36
790
发布2024-08-06 11:25:36
举报
文章被收录于专栏:开发工具

核心线程数过多:

如果核心线程数量设置得过高,可能会导致CPU过度调度,增加上下文切换的开销,甚至耗尽系统可用的线程资源。

最大线程数过大:

当任务队列满载且所有工作线程都在运行时,如果最大线程数设置得过高,可能会导致大量线程等待任务,浪费资源,甚至引发内存溢出。

线程池允许创建的最大线程数,当任务到达时,如果当前线程数小于最大线程数,即使核心线程都是空闲的,也会创建新的线程来处理任务。

当线程池的最大线程数设置得过大时,可能会对系统产生以下几方面的影响:

代码语言:javascript
复制
资源耗尽:
内存消耗增加:每个线程都会占用一定的堆栈空间,
线程数过多可能导致内存消耗过大,
甚至引发OutOfMemoryError。

CPU资源过度分配:
过多的线程会导致CPU过度调度,
增加上下文切换的频率,
降低CPU的利用率和整体系统的吞吐量。

性能下降:
上下文切换开销:
频繁的上下文切换会消耗大量的CPU时间,
降低线程的实际执行效率。

I/O等待:
如果线程执行的任务涉及I/O操作,
过多的线程在等待I/O完成时会占用更多的系统资源,
如文件句柄、网络连接等。

系统稳定性受影响:
死锁风险增加:
大量线程并发执行时,
资源竞争更加激烈,
增加了死锁的风险。

系统响应时间变长:
过多的线程导致系统调度负担加重,
响应用户请求的时间可能会显著增加。

管理复杂度提升:
监控和调试难度加大:
线程数过多使得跟踪和分析线程状态变得更加困难,
增加了问题定位和故障排查的复杂度。1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.

任务队列设置不当:

队列容量过大或无界:如果任务队列的容量设置得过大或者没有限制,当系统负载高时,可能会导致内存消耗过大,甚至发生OutOfMemoryError。

频繁的创建和销毁线程,毕竟线程是较重的资源,频繁的创建和销毁对系统性能是没好处的。

队列容量过小:

如果队列容量太小,可能会导致任务被拒绝执行,从而影响系统的正常运行。

任务执行时间过长:

如果提交到线程池的任务执行时间过长,而线程池的核心线程数又相对较少,可能会导致线程池中的所有线程都被长时间占用,无法处理新的任务请求,造成系统响应延迟或拒绝服务。

资源竞争 导致 常见的并发问题:

当多个线程同时访问共享资源时,如果没有正确的同步机制,可能会引发死锁或数据不一致的问题,导致系统不稳定。

资源争用(Resource Contention):

资源争用是指多个线程同时竞争有限的系统资源,导致性能下降。当多个线程同时请求同一个资源时,可能会出现资源争用问题。

代码语言:javascript
复制
//多个读取线程 和 一个写入线程 同时访问  共享的列表list。
//由于读取和写入都需要获取列表的锁,
//可能会导致读取线程和写入线程之间的资源争用,
//从而降低性能。


class YYExample {
    private static List<Integer> list = new ArrayList<>();
 
    public static void main(String[] args) {
        Runnable reader = () -> {
            while (true) {
                synchronized (list) {
                    for (Integer value : list) {
                        // 读取共享列表
                        System.out.println(value);
                    }
                }
            }
        };
 
        Runnable writer = () -> {
            while (true) {
                synchronized (list) {
                    // 写入共享列表
                    list.add(1);
                }
            }
        };
 
        // 创建多个读取线程和写入线程
        for (int i = 0; i < 50; i++) {
            new Thread(reader).start();
        }
        new Thread(writer).start();
    }
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.

死锁

`thread1` 先获取 `resource1`,然后尝试获取 `resource2`,

而 `thread2` 先获取 `resource2`,然后尝试获取 `resource1`。

如果这两个线程同时运行,它们可能会相互等待对方释放资源,导致死锁。

代码语言:javascript
复制
class DemoExample {
    private static Object resource1 = new Object();
    private static Object resource2 = new Object();
 
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread 1 acquired resource 1");
                synchronized (resource2) {
                    System.out.println("Thread 1 acquired resource 2");
                }
            }
        });
 
        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("Thread 2 acquired resource 2");
                synchronized (resource1) {
                    System.out.println("Thread 2 acquired resource 1");
                }
            }
        });
 
        thread1.start();
        thread2.start();
    }
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.

数据不一致

代码语言:javascript
复制
class Counter {
    private int count;
 
    public void increment() {
        count++;
    }
 
    public int getCount() {
        return count;
    }
}

在没有同步机制的情况下,
多个线程并发执行 `increment()` 方法可能导致竞态条件,
从而导致最终的计数结果不正确。1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.

异常处理不当:

如果线程在执行任务时抛出未捕获的异常,

且没有适当的异常处理机制,

可能会导致线程池中的线程停止工作,

减少可用线程的数量,

影响系统性能。

当线程在执行任务时抛出异常,正确的捕获和处理异常是保证程序稳定性和健壮性的关键。以下是在Java中如何捕获和处理线程中抛出的异常的几种方法:

1. 在Runnable或Callable接口实现中捕获异常

如果你的任务是通过实现Runnable或Callable接口来定义的,你可以在实现的方法中直接捕获异常。例如:

代码语言:javascript
复制
public class Task implements Runnable {
    @Override
    public void run() {
        try {
            // 执行可能抛出异常的任务
            doSomething();
        } catch (Exception e) {
            // 异常处理逻辑,如记录日志、发送警报等
            System.err.println("Task failed due to: " + e.getMessage());
            e.printStackTrace();
        }
    }

    private void doSomething() throws Exception {
        // 可能抛出异常的代码
    }
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.
2. 使用Future获取异常

当使用Callable接口并配合ExecutorService时,可以通过Future.get()方法获取任务的结果或抛出的异常:

代码语言:javascript
复制
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(new Task());

try {
    future.get(); // 这里会抛出任务中抛出的异常
} catch (ExecutionException e) {
    // 获取并处理原始异常
    Throwable cause = e.getCause();
    System.err.println("Task failed due to: " + cause.getMessage());
} catch (InterruptedException e) {
    // 处理中断异常
}1.2.3.4.5.6.7.8.9.10.11.12.
3. 设置UncaughtExceptionHandler

你可以为线程设置一个UncaughtExceptionHandler,当线程抛出未捕获的异常时,这个处理器会被调用来处理异常:

代码语言:javascript
复制
Thread thread = new Thread(new Task());
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    public void uncaughtException(Thread t, Throwable e) {
        // 异常处理逻辑
        System.err.println(t.getName() + " throws an exception: " + e.getMessage());
    }
});
thread.start();1.2.3.4.5.6.7.8.
4. 监听线程池异常

对于ExecutorService,可以监听其内部线程的异常。这通常需要自定义线程工厂并设置UncaughtExceptionHandler:

代码语言:javascript
复制
ThreadFactory factory = new ThreadFactoryBuilder()
    .setUncaughtExceptionHandler((t, e) -> {
        System.err.println(t.getName() + " throws an exception: " + e.getMessage());
    })
    .build();

ExecutorService executor = Executors.newFixedThreadPool(10, factory);1.2.3.4.5.6.7.

结论:

为了避免这些问题,合理配置线程池参数,监控线程池的状态,以及对任务进行适当的异常处理和资源管理是非常重要的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 核心线程数过多:
  • 最大线程数过大:
  • 任务队列设置不当:
  • 队列容量过小:
  • 任务执行时间过长:
  • 资源竞争 导致 常见的并发问题:
    • 资源争用(Resource Contention):
      • 死锁
        • 数据不一致
          • 1. 在Runnable或Callable接口实现中捕获异常
          • 2. 使用Future获取异常
          • 3. 设置UncaughtExceptionHandler
          • 4. 监听线程池异常
      • 异常处理不当:
      • 结论:
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档