JAVA线程池的血案_捕获线程池任务异常引发的血案

一、前言

今天遇到一个很诡异的问题,在多款Android 4.2.2版本的手机上发现处理后台任务的服务对前台请求毫无响应。这里的后台服务是一个RemoteService,目的为了处理更新&上传等任务。本来以为是跨进程组件间通讯出现兼容性问题,后来根据分析发现问题没有那么简单,这里记录下问题原因。

二、第一个错误

在后台服务处理任务请求时,使用了自定义线程池处理异步任务,而Java 线程池java.util.concurrent.ThreadPoolExecutor会Catch住所有异常,即便是你运行例如下面的代码也不会抛出异常。

1

2

3

4

5

6

7

8

9Runnable runnable = new Runnable() {

@Override

public void run() {

Test test = null;

System.out.println(test.toString());

}

};

mExecutor.submit(runnable);

在处理线程池异常捕获的问题时,犯下了第一个错误,使用了submit方法拿到任务执行结果会阻塞当前线程。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16public Future> submit(Runnable task) {

Future> future = mExecutor.submit(task);

try {

future.get();

} catch (InterruptedException e) {

Thread.currentThread().interrupt(); // Reset interrupted status

} catch (ExecutionException e) {

Throwable exception = e.getCause();

// Forward to exception reporter

if (exception instanceof UncatchException) {

throw (UncatchException) exception;

}

}

return future;

}

三、第二个错误

在错误一的基础上,使用了一个开源并发库android-lite-go,先看它的异步任务是如何调度的。

1

2

3

4

5

6

7

8

9

10

11

12

13int coreSize = CPU_CORE;

int queueSize = coreSize * 32;

synchronized (lock) {

if (runningList.size() < coreSize) {

runningList.add(scheduler);

threadPool.execute(scheduler);

} else if (waitingList.size() < queueSize) {

waitingList.addLast(scheduler);

} else {

//...

}

}

当正在运行的任务数小于设定的coreSize时,submit的任务会提交到线程池。

coreSize由系统核心数确定。

当正在运行的任务数小于queueSize时,submit的任务会提交到等待队列中。

当一个任务执行完后,会从等待队列中获取一个任务提交给线程池执行。

在一个异步任务中使用阻塞当前线程的方法,把另一个任务提交给了executor。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23private void runTaskOne() {

Runnable runnable = new Runnable() {

@Override

public void run() {

// do something

runTaskTwo();

}

};

mTaskController.submit(runnable);

}

private void runTaskTwo() {

Runnable runnable = new Runnable() {

@Override

public void run() {

// do task2

// ......

}

};

mTaskController.submit(runnable);

}

三、问题原因及解决方案

当正在运行的任务使用阻塞当前进程的submit方法提交另一个异步任务时,后提交的任务被放入等待队列等待线程池执行,但在Running list中的任务又在等待后任务执行的结果,这样就造成了死锁!!这个问题跟Android系统版本并没有关系,但因为Android 4.2.2版本的手机低端较多核心数较少,所以runningList的大小就比较小,Running List很容易就被填满。

34d4cd2eaaee87b8fc7179610a1dd79a.png

解决方案使用非阻塞的方法提交异步任务

自定义线程池,重写afterExecute()方法,在该中获取线程池运行的异步任务运行时异常。

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

27ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(11, 100, 1, TimeUnit.MINUTES, //

new ArrayBlockingQueue(10000),//

new DefaultThreadFactory()) {

protected void afterExecute(Runnable r, Throwable t) {

super.afterExecute(r, t);

printException(r, t);

}

};

private static void printException(Runnable r, Throwable t) {

if (t == null && r instanceof Future>) {

try {

Future> future = (Future>) r;

if (future.isDone())

future.get();

} catch (CancellationException ce) {

t = ce;

} catch (ExecutionException ee) {

t = ee.getCause();

} catch (InterruptedException ie) {

Thread.currentThread().interrupt(); // ignore/reset

}

}

if (t != null)

log.error(t.getMessage(), t);

}

四、参考文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值