文档章节

你的java代码可中断吗?(2)

writeademo
 writeademo
发布于 2017/06/01 17:53
字数 1477
阅读 19
收藏 0

 

 

 

1.确保提交到线程池的任务可中断
 

原文:www.securecoding.cert.org,TPS02-J. Ensure thattasks submitted to a thread pool are interruptible。

  

为了能完全关闭线程池或者取消线程池中的个别任务,程序应提交支持使用Thread.interrupt()中断的任务到线程池。程序不应提交不支持中断的任务到线程池,除非是无阻塞且短时间运行的任务。

 

根据java.util.concurrent.ExecutorService.shutdownNow() Java API的描述:

尝试终止所有正在执行的任务,停止所有等待处理的任务,并且返回等待执行的任务列表…

 

除了尽最大努力尝试终止正在执行的任务之外,没有任何其他的保证。例如,典型实现是通过Thread.interrupt()进行取消,因此任何不能中断的任务可能永远不会被终止。

 

违规代码示例(Shutting Down Thread Pools)

如下是违规代码示例,提交SocketReader任务到PoolService声明的线程池中:

 

 

因为任务不支持Thread.interrupt()进行中断,且因为shutdown()方法必须等到所有正执行的任务执行完成,所以shutdownNow()方法可能不能立即完全关闭线程池。

 

同样,某些不用Thread.interrupted() 而使用其他的机制来决定何时关闭的任务,其是无法响应shutdown()和shutdownNow()。例如,通过检查一个volatile标志位来决定是否可安全关闭的任务,对这些方法是无法响应的。THI05-J. Do not use Thread.stop() to terminate threads提供了使用标志位终止线程的更多信息。

 

合规解决方案(Submit InterruptibleTasks)

如下合规解决方案定义了SocketReader的一个可中断的版本,实例化并提交到线程池:

 

 

2.持有锁时不要执行阻塞操作

原文:www.securecoding.cert.org,LCK09-J.Do not perform operations that canblock while holding a lock

 

持有锁时执行耗时或阻塞操作会严重降低系统性能,且可能导致饥饿。此外,死锁会导致相互依赖的线程无限阻塞。阻塞操作包括网络、文件、控制台I/O(如Console.readLine())和对象序列化,无限延迟线程执行也算是一种阻塞操作(如通过Thread.sleep())。因此,在持有锁时,程序不应该执行阻塞操作。

 

当Java虚拟机(JVM)与不可靠网络、文件I/O交互时,可能引起巨大的性能损耗。在这种情况下,应避免持有锁时执行网络上的文件I/O操作。在等待输出流锁或I/O完成事件的文件操作(如日志)会阻塞,可以在一个单独的线程中执行来加速任务处理。记录请求日志可以添加到一个队列,与直接文件I/O相比,队列put()操作会增加一点小的开销。

 

违规代码示例(Deferring a Thread)

如下违规代码示例定义了一个接受timeout参数的工具类:

 

方法是synchronized,当线程休眠后其他线程不能使用synchronized的方法。当前对象的监视器并未被释放,这是因为Thread.sleep() 方法没有同步语义。

 

合规解决方案(Intrinsic Lock)                  

如下合规解决方案定义了doSomething()方法,其带有一个timeout参数而不是time参数。使用Object.wait()代替Thread.sleep(),Object.wait()允许设置一个通知的超时周期,超时后可以唤醒线程。

 

进入到等待状态后当前对象的监视器会立即释放。当超时后,线程会在重新获取到当前对象的监视器后恢复继续执行。

 

根据Object类Java API的描述:

wait方法,它将当前线程放入到该对象的等待集,且只有该对象能释放锁;当线程等待时,当前线程上的任何synchronized的其他对象将保持锁定。

 

此方法应仅由该对象监视器拥有者线程调用。

 

程序必须确保持有其他对象锁的线程在进入等待状态之前适时释放这些锁。等待和通知的一些额外帮助可见 THI03-J. Always invoke wait() and await() methods insidea loop 和 THI02-J. Notify all waiting threads rather than a single thread。

 

违规代码示例(Network I/O)

如下违规代码示例定义了sendPage()方法,其从服务端向客户端发送一个Page对象。该方法是synchronized,在多线程请求并发访问时来来保护 pageBuff数组。

public class SendPage {
 private final int MAX_PAGE_SIZE = 10;
 Page[] pageBuff = new Page[MAX_PAGE_SIZE];

 public class Page {
  int code;
  String message;
  Object page;
  String pageName;

  public String getName() {
   // TODO Auto-generated method stub
   return pageName;
  }
 }

 public synchronized boolean sendPage(Socket socket, String pageName) throws IOException {
  ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
  Page targetPage = null;
  for (Page page : pageBuff) {
   if (page.getName().compareTo(pageName) == 0) {
    targetPage = page;
   }
  }
  if (targetPage == null) {
   return false;
  }
  out.writeObject(targetPage);
  out.flush();
  out.close();
  return true;

 }
}

 

在synchronized的sendPage()方法中调用writeObject()会造成延迟和死锁,像在高延迟网络或当网络连接有损(丢包)情况时。

合规解决方案

如下合规解决方案将整个过程拆分为如下步骤:

1.    在数据结构上执行的操作需要同步;

2.    创建对象的副本用于发送;

3.    在单独的无同步方法中执行网络调用。

 

public class SendPageCanStop {
 private final int MAX_PAGE_SIZE = 10;
 Page[] pageBuff = new Page[MAX_PAGE_SIZE];

 public class Page {
  int code;
  String message;
  Object page;
  String pageName;

  public String getName() {
   // TODO Auto-generated method stub
   return pageName;
  }
 }

 public boolean sendPage(Socket socket, String pageName) throws IOException {
  Page targetPage = getPage(pageName);
  if (targetPage == null) {
   return false;
  }
  return deliverPage(socket, targetPage);
 }

 private synchronized Page getPage(String pageName) {
  Page targetPage = null;
  for (Page p : pageBuff) {
   if (p.getName().compareTo(pageName) == 0) {
    targetPage = p;
   }

  }
  return targetPage;
 }

 private boolean deliverPage(Socket socket, Page page) {
  ObjectOutputStream out = null;
  boolean result = true;
  try {
   out = new ObjectOutputStream(socket.getOutputStream());
   out.writeObject(page);
   out.flush();
   out.close();

  } catch (Exception e) {
   result = false;
  } finally {
   try {
    if (out != null) {
     out.close();
    }
   } catch (Exception e2) {
    result = false;
   }

  }
  return result;
 }

在该合规解决方案中,无同步的sendPage()方法调用同步的getPage()方法从pageBuff 数组获取请求的Page。在Page取出后,sendPage()调用无同步的deliverPage()方法将Page交付给客户端。

 

 

 

本文转载自:http://mp.weixin.qq.com/s/YQKFmbtKAe70tj3oRYyfSQ

共有 人打赏支持
writeademo
粉丝 25
博文 555
码字总数 205067
作品 0
东城
私信 提问
多线程编程读书笔记之线程中断的本质

Java试图提供过抢占式限制中断,但问题多多,例如已被废弃的Thread.stop、Thread.suspend和 Thread.resume等。另一方面,出于Java应用代码的健壮性的考虑,降低了编程门槛,减少不清楚底层机...

刘学炜
2012/07/03
0
0
JVM技术交流分享 · 第1期

JVM技术每周分享 · 第1期 JVM技术每周分享整理了JVM技术交流群每周讨论的内容,由群内成员整理归纳而成。如果你有兴趣入群讨论,请关注「Java技术精选」公众号,通过右下角菜单「入群交流」...

陈树义
11/19
0
0
Java多线程之interrupt()的深度研究

原文地址:http://www.cnblogs.com/carmanloneliness/p/3516405.html 近期学习Java多线程的中断机制,网上的帖子说得很浅,并没深究其原理。看了Java源码,对Java的中断机制有了略深入的理解...

恶魔在江湖
2014/02/18
0
0
Java并发编程笔记之基础总结(二)

一.线程中断 Java 中线程中断是一种线程间协作模式,通过设置线程的中断标志并不能直接终止该线程的执行,而是需要被中断的线程根据中断状态自行处理。   1.void interrupt() 方法:中断线...

狂小白
07/17
0
0
java并发编程(六): 取消与关闭

取消与关闭: 如何正确,安全地取消或关闭任务。 任务取消: 若外部代码能在某个操作正常完成之前将其置入“完成”状态,则还操作是可取消的。 取消操作的原因: 1. 用户请求取消。 2. 有时间...

ihaolin
2014/03/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

微服务分布式事务实现

https://www.processon.com/view/link/5b2144d7e4b001a14d3d2d30

WALK_MAN
今天
2
0
《大漠烟尘》读书笔记及读后感文章3700字

《大漠烟尘》读书笔记及读后感文章3700字: 在这个浮躁的社会里,你有多久没有好好读完一本书了? 我们总觉得自己和别人不一样,所以当看到别人身上的问题时,很少有“反求诸己”,反思自己。...

原创小博客
今天
4
0
大数据教程(9.5)用MR实现sql中的jion逻辑

上一篇博客讲解了使用jar -jar的方式来运行提交MR程序,以及通过修改YarnRunner的源码来实现MR的windows开发环境提交到集群的方式。本篇博主将分享sql中常见的join操作。 一、需求 订单数据表...

em_aaron
今天
3
0
十万个为什么之什么是resultful规范

起源 越来越多的人开始意识到,网站即软件,而且是一种新型的软件。这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时(high latency)、高并发等特点...

尾生
今天
3
0
Terraform配置文件(Terraform configuration)

Terraform配置文件 翻译自Terraform Configuration Terraform用文本文件来描述设备、设置变量。这些文件被称为Terraform配置文件,以.tf结尾。这一部分将讲述Terraform配置文件的加载与格式。...

buddie
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部