疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

java培训:Runnable中的运行时的异常


 

   Java是一门面向对象语言,在我们的java编程中相信大家都会遇到各种各样的问题,很让我们迷茫,现在java 培训的老师将为你介绍一段由于dba操作不当造成系统打印RejectedExecutionException的异常的原因。类似的代码是这样的:

 

  Java代码

  ThreadPoolExecutor workers = newThreadPoolExecutor(10, 600, 30, TimeUnit.SECONDS, newArrayBlockingQueue(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,而队列也放满了待处理的task的时候,ThreadPoolExecutor就会去创建一个新的线程。直至达到线程池中的消费线程达到maximumPoolSize(也就是上面代码里的600)。如果这个时候再有task加入,根据默认的饱和策略,将会抛出RejectedExecutionException异常。

  这次就是由于获取数据库等待时间超长,导致task响应时间变慢,继而线程池中活跃的消费线程堆积到600个线程依然无法应付,才抛出的RejectedExecutionException。

  显然抛出RejectedExecutionException不是那么的友好,我们在这里可以自定义饱和策略。默认系统饱和策略是抛出异常。

  Java代码

  workers.setRejectedExecutionHandler(new RejectedExecutionHandler() {

  @Override

  public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {

  }

  });

  另外当线程故障恢复时,通过日志惊奇的观察到,队列中的线程以串行的方式运行了一短时间,不过很快就正常了。于是在本地写了一个测试。

  Java代码

  ThreadPoolExecutor workers = new ThreadPoolExecutor(10, 100, 3, TimeUnit.SECONDS, new ArrayBlockingQueue(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()));

  }

  运行如上的代码,打印日志如下:

  Java代码

  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。

  这就是解决这个问题的方法了。对于java开发者来说得注意开发过程中遇到的问题,java培训哪里好呢?首选广州疯狂java。如果你还想学习java知识,欢迎到疯狂java官方网站下载java视频教程!