文档章节

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

writeademo
 writeademo
发布于 2017/06/01 17:53
字数 1477
阅读 16
收藏 0
点赞 0
评论 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
粉丝 22
博文 439
码字总数 167648
作品 0
东城
Java 使用 happen-before 规则实现共享变量的同步操作

前言 熟悉 Java 并发编程的都知道,JMM(Java 内存模型) 中的 happen-before(简称 hb)规则,该规则定义了 Java 多线程操作的有序性和可见性,防止了编译器重排序对程序结果的影响。按照官方的...

stateIs0 ⋅ 01/20 ⋅ 0

每天理解一点Linux内核之进程的四个要素

1.程序 程序是静态的代码,而进程是运行的程序。程序好比是剧本,而进程就是戏,戏是按照剧本演的。 2.私有财产 每个进程都有自己专有的系统堆栈空间 3.户口 在内核中有一个task_struct数据结...

u010278923 ⋅ 04/11 ⋅ 0

Java内存模型之happens-before和重排序

重排序 在执行程序时,为了提供性能,处理器和编译器常常会对指令进行重排序,满足以下两个条件: 在单线程环境下不能改变程序运行的结果; 存在数据依赖关系的不允许重排序 happens - befo...

细节探索者 ⋅ 05/02 ⋅ 0

关于编写Java程序让Jvm崩溃

  今天在书上看到一个作者提出一个问题"怎样通过编写Java代码让Jvm崩溃",我看了之后也不懂。带着问题查了一下,百度知道里面有这样一个答案:   1 package jvm;   2   3 public clas...

thinkyoung ⋅ 2014/11/01 ⋅ 0

14、Java并发性和多线程-Java ThreadLocal

以下内容转自http://ifeve.com/java-theadlocal/: Java中的ThreadLocal类可以让你创建的变量只被同一个线程进行读和写操作。因此,尽管有两个线程同时执行一段相同的代码,而且这段代码又有...

easonjim ⋅ 2017/06/16 ⋅ 0

计算机科学中抽象的好处与问题—伪共享实例分析

David John Wheeler有一句名言“计算机科学中的任何问题都可以通过加上一层间接层来解决”,一层不够就再加一层。后半句是我加的 (* ̄︶ ̄) ,虽然有点玩笑的意思,但是也的确能说明一些问题...

MageekChiu ⋅ 01/10 ⋅ 0

Spring boot 2.0 之优雅停机

spring boot 框架在生产环境使用的有一段时间了,它“约定大于配置”的特性,体现了优雅流畅的开发过程,它的部署启动方式()也很优雅。但是我使用的停止应用的方式是 ,即使写了脚本,还是...

rabbitGYK ⋅ 05/20 ⋅ 0

sharding-jdbc源码分析—准备工作

原文作者:阿飞Javaer 原文链接:https://www.jianshu.com/p/7831817c1da8 接下来对sharding-jdbc源码的分析基于tag为源码,根据sharding-jdbc Features深入学习sharding-jdbc的几个主要特性...

飞哥-Javaer ⋅ 05/03 ⋅ 0

【Canal源码分析】Canal Instance启动和停止

一、序列图 1.1 启动 1.2 停止 二、源码分析 2.1 启动 这部分代码其实在ServerRunningMonitor的start()方法中。针对不同的destination,启动不同的CanalInstance。主要的方法在于initRunning...

端木轩 ⋅ 05/24 ⋅ 0

java编程语言学习:异常处理

Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互...

Java小辰 ⋅ 06/01 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

思路分析 如何通过反射 给 bean entity 对象 的List 集合属性赋值?

其实 这块 大家 去 看 springmvc 源码 肯定可以找到实现办法。 因为 spirngmvc 的方法 是可以 为 对象 参数里面的 list 属性赋值的。 我也没有看 具体的 mvc 源码实现,我这里只是 写一个 简...

之渊 ⋅ 32分钟前 ⋅ 0

vim使用手册--配对括号的查找

1、把光标放在标记有(、[或{处。 2、按%字符 3、此时光标的位置应当在配对的括号处 4、再次按%就可以跳回配对的第一个括号处。

dragon_tech ⋅ 36分钟前 ⋅ 0

c++ 、object-c printf,%02X和%x有什么区别 ?

%x即按十六进制输出,英文字母小写,右对齐。 %02X有以下变化:英文字母变大写,如果输出字符不足两位的,输出两位宽度,右对齐,空的一位补0。超过两位的,全部输出。 如果不用 %02x 会出现...

yizhichao ⋅ 41分钟前 ⋅ 0

Spring源码解析(七)——实例创建(中)

前言 上一节讲到了,Spring 会根据实例的作用域执行不同的创建逻辑,分别是 Singleton、Prototype、其他 Scope,其中 Singleton 会调用 getSingleton 从缓存中获取,缓存中没有才会创建实例;...

MarvelCode ⋅ 41分钟前 ⋅ 0

Thrift RPC实战(六) spring集成thrift

1.服务端设置 对泛型Thrift Service的支持, 通过采用spring配置以及反射的方式来实现.对于一个服务提供者来说,需要提供端口,接口以及接口实现类,因此在接口中spring配置文件中配置如下 <!...

lemonLove ⋅ 43分钟前 ⋅ 0

oracle11g自动分区使用

为什么使用自动分区? 在oracle11g之前,oracle是不支持自动分区功能的,这就可能导致我们系统在运行一段时间之后,就需要看看分区是否创建或者写触发器进行创建分区,否则就会导致数据无法入...

strict_nerd ⋅ 55分钟前 ⋅ 0

Spring mvc ViewResolver视图解析器实现机制

概要 我们在controller里面经常这样return一个ModelAndView。 return new ModelAndView("userList", "users", userList); DispatcherServlet 靠 ViewResolver 把 userList 解析为 /WEB-INF......

轨迹_ ⋅ 今天 ⋅ 0

策略模式

1.策略模式 策略模式是同一个行为的不同处理办法。策略模式和简单工厂模式的区别:1.策略模式主要是方法的执行方式,工厂模式要获取的对象。两者的侧重点不同。 ...

Cobbage ⋅ 今天 ⋅ 0

行政区划代码转为字典形式

原数据为: http://www.mca.gov.cn/article/sj/xzqh/2018/201804-12/201804-06041553.html 手动替换了一下格式,并使用下面的代码处理. # 输入格式s = """110000:北京市110101:东城区1101...

漫步海边小路 ⋅ 今天 ⋅ 0

android apk 签名

创建key,需要用到keytool.exe (位于C:\Program Files\Java\jdk1.6.0_10\bin目录下),使用产生的key对apk签名用到的是jarsigner.exe (位于C:\Program Files\Java\jdk1.6.0_10\bin目录下),把...

国仔饼 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部