前段时间,夜晚突然收到报警,紧急上线排查。由于dba操作不当,大片数据回滚,发生锁表的情况,请求返回时间过长,使得系统打印出大量的RejectedExecutionException的异常。定位到代码片段类似:
ThreadPoolExecutor workers = new ThreadPoolExecutor(10, 600, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
ShareTask shareTask = new ShareTask(shareInfo, clientId, rtnResult,
outerShareContext, importOuterFriend, bindingManager,shareUserManager);
workers.execute(shareTask);
这里就要说说ThreadPoolExecutor和ArrayBlockingQueue了,众所周知ArrayBlockingQueue类是一个阻塞的队列。当和ThreadPoolExecutor使用时,ThreadPoolExecutor会在初始化时开启corePoolSize(也就是上面代码中的10)个线程,去消费队列里的task。当并发量增大,直到corePoolSize全都在执行task,ThreadPoolExecutor不会立即新建消费线程,而是将新加入的task放入到对队列中,一直到放满,ThreadPoolExecutor才会去创建一个新的线程。直至达到线程池中的消费线程达到maximumPoolSize(也就是上面代码里的600)。如果这个时候再有task加入,根据默认的饱和策略,将会抛出RejectedExecutionException异常。
这次就是由于获取数据库等待时间超长,导致task响应时间变慢,继而线程池中活跃的消费线程堆积到600个线程依然无法应付,才抛出的RejectedExecutionException。
显然抛出RejectedExecutionException不是那么的友好,我们在这里可以自定义饱和策略。默认系统饱和策略是抛出异常。
workers.setRejectedExecutionHandler(new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
}
});
另外当线程故障恢复时,通过日志惊奇的观察到,队列中的线程以串行的方式运行了一短时间,不过很快就正常了。于是在本地写了一个测试。
ThreadPoolExecutor workers = new ThreadPoolExecutor(10, 100, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
for (int i = 0; i < 110; i++) {
workers.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
throw new RuntimeException();
}
});
}
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
System.out.println(String.format("task count:%s; active count:%s", workers.getTaskCount(), workers.getActiveCount()));
}
运行如上的代码,打印日志如下:
task count:110; active count:100
task count:10; active count:1
task count:10; active count:1
task count:9; active count:1
task count:9; active count:1
...
task count:2; active count:1
task count:2; active count:1
task count:1; active count:1
task count:1; active count:1
可以看到,最后的10个task是以类似串行的方式在运行。
这里的原因在于ThreadPoolExecutor的中断策略,当Runnable中抛出RTE时,ThreadPoolExecutor会将执行当前的Runnable的线程Dead。由于例子的代码100%会抛出RTE,最终的结果就是ThreadPoolExecutor中存活的消费线程数变为0。ThreadPoolExecutor创建线程只有在初始化,调用excute等几个主动方法中才会去做,我们任务的提交早在一开始就已经做了,最终导致的结果就是永远只存在一个线程服务这个任务队列。
那么要比较优雅的解决这个问题,可以使用FutureTask,FutureTask里面会处理运行时异常,不会将其抛出给ThreadPoolExecutor。
分享到:
相关推荐
4. 关键在于,当不再需要执行Runnable或者需要停止Handler时,我们可以在`run`方法内部调用`handler.removeCallbacksAndMessages(null)`,这将移除Handler上所有的回调(包括Runnable)和消息,有效地停止了Handler...
本文主要介绍了 Java 中多线程异常捕获 Runnable 的实现,包括背景、工具、思路、代码和结果等方面的内容。 一、背景 在 Java 多线程编程中,子线程抛出的异常不会被主线程捕获,这使得我们很难对子线程的异常进行...
`Runnable`接口是Java提供的一个核心机制,用于创建并运行线程。本实例将深入讲解如何使用`Runnable`接口来实现多线程,并通过具体的`TestRunnable.java`源代码进行演示。 一、`Runnable`接口简介 在Java中,`...
在实现Runnable接口时,我们需要重写`run()`方法,这是线程运行的主要逻辑所在。当线程被启动(调用Thread对象的start()方法)时,系统会自动调用`run()`方法。在这个Java小游戏的例子中,`run()`方法可能包含了游戏...
Java中的Runnable、Callable、Future和FutureTask是Java多线程编程中的核心概念,它们各自扮演着不同的角色,共同协作以实现并发任务的管理和执行。 1. **Runnable**: Runnable是最基本的多线程接口,它只有一个`...
在Java编程语言中,多线程的实现有多种方式,其中最常见的是通过继承`Thread`类和实现`Runnable`接口。这两种方法各有其特点和适用场景,理解它们之间的区别对于编写高效、可维护的多线程应用至关重要。 ### 继承...
在Java中创建线程有两种方法:使用Thread类和使用Runnable接口。在使用Runnable接口时需要建立一个Thread实例。因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例。
由于Java的单继承特性,当一个类需要继承其他类时,就不能再直接继承`Thread`,这时实现`Runnable`接口就成为创建线程的优选方案。本篇将深入探讨如何通过实现`Runnable`接口来创建多线程,以及如何使用`Thread`类的...
当这个任务被放入Thread对象中并启动时,`run()`方法会被执行。 创建一个Runnable接口实现类的基本步骤如下: 1. 定义一个类实现Runnable接口。 2. 在实现类中重写`run()`方法,编写线程运行的代码。 3. 创建...
此外,每个`Thread`对象都有自己的运行时数据,如成员变量,这可能导致内存的浪费。 总结来说,`Runnable`接口更适合那些希望保持类的继承结构不受影响或者需要数据共享的情况,而直接继承`Thread`类则适用于简单的...
- `Thread`:直接在`Thread`的`run()`方法中抛出异常时,可以在这个方法内部进行处理,提供了更多的控制权。 总的来说,`Runnable`更适合于需要多线程共享资源的情况,同时也更符合面向对象的设计原则。而`Thread`...
Thread 线程类是 Java 中 Thread 和 Runnable 接口的实现,它提供了线程的基本操作和管理。下面是对 Thread 类的详细介绍: 1. Runnable 接口 Runnable 接口是 Thread 类的基类,它提供了 run() 方法,该方法是...
当需要在后台线程执行一段代码时,可以创建一个实现了`Runnable`接口的类实例,并将这个实例传递给可以运行它的线程。`run()`方法中的代码就是实际要执行的任务。 其次,`Handler`是Android中的消息处理机制的一...
Autosar SWC在Simulink中设置多个runnable的测试模型及生成的代码及Arxml文件
在一个线程中求100以内的偶数,求出一个偶数后休眠一个随机时间在(1-300毫秒之间).在另一个线程中求奇数,求出一个奇数后也休眠一个随机时间(1-300毫秒之间).输出数据时应有提示,指明是哪个线程输出的数据 用实例...
在Android开发中,多线程下载是一个常见的需求,特别是在处理大文件或者为了提高用户体验时,我们需要利用多线程来分块下载数据。本篇将基于`Runnable`接口提供一个简单的多线程下载示例,帮助开发者理解如何在...
- 如果需要让一个类同时继承其他类,并且希望该类能作为线程运行,应该实现`Runnable`接口。 2. **资源共享问题**: - 使用`Runnable`可以更好地实现资源共享。 - 例如,在售票系统中,多个线程可以共享同一个`...
`Runnable`是Java多线程编程的核心组件,它允许我们定义一个可执行的任务,然后在不同的线程上运行。 首先,我们需要创建一个新的Android项目,并在`main.xml`布局文件中添加一个TextView,用于显示数字时钟的时间...
1. 什么是Runnable接口: 1.1 介绍Runnable接口 1.2 与Thread类的对比 2. 创建线程的方式: 2.1 继承Thread类 2.2 实现Runnable接口 3. 实现Runnable接口: 3.1 实现步骤 3.2 优点与用途 4. 启动线程: 4.1 使用...