文档章节

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

writeademo
 writeademo
发布于 2017/06/01 17:53
字数 1477
阅读 16
收藏 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
粉丝 23
博文 492
码字总数 181345
作品 0
东城
多线程编程读书笔记之线程中断的本质

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

刘学炜
2012/07/03
0
0
Java并发编程笔记之基础总结(二)

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

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

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

ihaolin
2014/03/30
0
0
Java多线程之interrupt()的深度研究

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

恶魔在江湖
2014/02/18
0
0
java远程调试与JVM调优工具

Java远程调试方法: 1、被调试程序当作调试服务器(本地主动连接远程服务器,需要用如下命令让远程服务器jvm开启调试模式)。 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8765 ...

chape
2013/12/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

[雪峰磁针石博客]软件测试专家工具包1web测试

web测试 本章主要涉及功能测试、自动化测试(参考: 软件自动化测试初学者忠告) 、接口测试(参考:10分钟学会API测试)、跨浏览器测试、可访问性测试和可用性测试的测试工具列表。 安全测试工具...

python测试开发人工智能安全
今天
2
0
JS:异步 - 面试惨案

为什么会写这篇文章,很明显不符合我的性格的东西,原因是前段时间参与了一个面试,对于很多程序员来说,面试时候多么的鸦雀无声,事后心里就有多么的千军万马。去掉最开始毕业干了一年的Jav...

xmqywx
今天
2
0
Win10 64位系统,PHP 扩展 curl插件

执行:1. 拷贝php安装目录下,libeay32.dll、ssleay32.dll 、 libssh2.dll 到 C:\windows\system32 目录。2. 拷贝php/ext目录下, php_curl.dll 到 C:\windows\system32 目录; 3. p...

放飞E梦想O
今天
0
0
谈谈神秘的ES6——(五)解构赋值【对象篇】

上一节课我们了解了有关数组的解构赋值相关内容,这节课,我们接着,来讲讲对象的解构赋值。 解构不仅可以用于数组,还可以用于对象。 let { foo, bar } = { foo: "aaa", bar: "bbb" };fo...

JandenMa
今天
1
0
OSChina 周一乱弹 —— 有人要给本汪介绍妹子啦

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子 :分享水木年华的单曲《中学时代》@小小编辑 手机党少年们想听歌,请使劲儿戳(这里) @须臾时光:夏天还在做最后的挣扎,但是晚上...

小小编辑
今天
54
8

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部