文档章节

你的java代码可中断吗?在连接中

writeademo
 writeademo
发布于 2017/05/31 11:44
字数 1722
阅读 14
收藏 0
点赞 0
评论 0

原文:www.securecoding.cert.org,THI04-J. Ensure thatthreads performing blocking operations can be terminated。

来自开涛的博客:http://mp.weixin.qq.com/s/RPX9GygSuCx0f84BsZOIxw

 

在网络或文件I/O上操作阻塞的线程和任务,必须给调用者提供一种明确终止执行的机制,防止出现拒绝服务(DoS)漏洞。

 

违规代码示例(Blocking I/O, Volatile Flag)

该违规代码示例使用了 THI05-J. Do not use Thread.stop() to terminate threads中的建议,使用一个volatile变量done判断是否安全的终止了线程。 不过当线程被网络I/O上的readLine() 操作阻塞时,在网络I/O完成之前,它是不能响应心设置的done变量的。因此,线程终止操作可以被无限期的延迟,直到readLine()网络I/O完成。

 

public class SocketReader implements Runnable{

       private final Socket socket;

       private final BufferedReader in;

       private volatile boolean done=false;

       private final Object lock=new Object();

       public SocketReader(String host,int port) throws IOException{

              this.socket=new Socket(host,port);

              this.in=new BufferedReader(new InputStreamReader(this.socket.getInputStream()));

             

       }

       @Override

       public void run() {

              try{

                     synchronized(lock){

                            readData();

                     }

              }catch(IOException in){

                     //forward to handler

              }

       }

      

       public void readData() throws IOException{

              String string;

              while(!done&& (string=in.readLine())!=null){

                     //Blocks until end of stream(null)

              }

             

       }

       public void shutDown(){

              done=true;

       }

       public static void main(String[] args) throws IOException, InterruptedException {

              SocketReader reader=new SocketReader("somehost",25);

              Thread thread=new Thread(reader);

              thread.start();

              Thread.sleep(1000);

              reader.shutDown();

             

       }

 

}

 

 

违规代码示例(Blocking I/O, Interruptible)

该违规代码示例和上面的例子类似,但使用线程中断来终止线程。java.net.Socket上的网络I/O操作是阻塞的,线程中断引起的线程终止操作也可以被无限期的延迟。

 

public class SocketReader1 implements Runnable {

       // other methods...

 

       private final Socket socket;

       private final BufferedReader in;

       private final Object lock = new Object();

 

       public SocketReader1(String host, int port) throws IOException {

              this.socket = new Socket(host, port);

              this.in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));

 

       }

 

       public void readData() throws IOException {

              String string;

              while (!Thread.interrupted() && (string = in.readLine()) != null) {

                     // Blocks until end of stream(null)

 

              }

       }

 

       @Override

       public void run() {

              try {

                     synchronized (lock) {

                            readData();

                     }

              } catch (IOException in) {

                     // forward to handler

              }

       }

 

       public static void main(String[] args) throws IOException, InterruptedException {

              SocketReader reader = new SocketReader("somehost", 25);

              Thread thread = new Thread(reader);

              thread.start();

              thread.sleep(1000);

              thread.interrupt();

       }

 

}

 

 

合规解决方案(Close Socket Connection)

合规解决方案是通过在shutdown方法关闭socket来终止阻塞的网络I/O操作。当socket关闭后,readLine()方法会抛出SocketException异常,从而继续线程中的其他处理。注意,想要即保持连接存活,同时干净地立即停止线程,这是不可能的。

 

public class SocketReaderRight implements Runnable{

       private final Socket socket;

       private final BufferedReader in;

       private volatile boolean done=false;

       private final Object lock=new Object();

       public SocketReaderRight(String host,int port) throws IOException{

              this.socket=new Socket(host,port);

              this.in=new BufferedReader(new InputStreamReader(this.socket.getInputStream()));

             

       }

       @Override

       public void run() {

              try{

                     synchronized(lock){

                            readData();

                     }

              }catch(IOException in){

                     //forward to handler

              }

       }

      

       public void readData() throws IOException{

              String string;

              try {

                     while((string=in.readLine())!=null){

                            //Blocks until end of stream(null)

                     }

              } catch (Exception e) {

                     shutDown();

              }

             

             

       }

       public void shutDown() throws IOException{

              socket.close();

       }

       public static void main(String[] args) throws IOException, InterruptedException {

              SocketReader reader=new SocketReader("somehost",25);

              Thread thread=new Thread(reader);

              thread.start();

              Thread.sleep(1000);

              reader.shutDown();

             

       }

 

}

当从main()方法调用了shutdown()方法后,readData()中的finally块又调用了一次shutdown(),再次关闭socket。这种情况不用担心,当socket已经被关闭,再次关闭不作任何操作。

 

当执行异步I/O时,可以调用java.nio.channels.Selector的close()或者wakeup()来立即中断java.nio.channels.Selector上的阻塞操作。(译者注:可以参考笔者的下一篇)

 

当出现阻塞状态之后必须执行额外的操作,可以使用一个boolean值标识等待终止。当需要用这种标志补全代码时,shutdown()方法应设置标志为false从而让线程退出while循环。

 

合规解决方案(InterruptibleChannel)

该合规解决方案没使用Socket连接而是使用一个可中断channeljava.nio.channels.SocketChannel。如果线程执行的网络I/O在读取数据时使用Thread.interrupt() 方法中断,线程会收到一个ClosedByInterruptException异常,且channel立即关闭,线程状态状态也会被设置。

 

public class SocketReaderRight1 implements Runnable{

       private final SocketChannel sc;

       private final String host;

       private final int port;

 

       private final Object lock=new Object();

       public SocketReaderRight1(String host,int port) throws IOException{

              this.host=host;

              this.port=port;

              this.sc=SocketChannel.open(new InetSocketAddress(host,port)); 

             

       }

       @Override

       public void run() {

              ByteBuffer buf=ByteBuffer.allocate(1024);

             

              try{

                     synchronized(lock){

                            while(!Thread.interrupted()){

                                   sc.read(buf);

                                   //...

                            }

                     }

              }catch(IOException ie){

                     //forward to handler

              }

       }

      

 

       public static void main(String[] args) throws IOException, InterruptedException {

              SocketReader reader=new SocketReader("somehost",25);

              Thread thread=new Thread(reader);

              thread.start();

              Thread.sleep(1000);

              thread.interrupt();

             

       }

}

此技术中断当前线程。然而,它停止线程只是通过轮询 Thread.interrupted() 方法的线程状态状态并在线程中断状态时终止线程实现的。使用SocketChannel  时要确保while循环中的条件在收到中断时立即进行测试,即使读取通常是一个阻塞操作。同样地,调用 java.nio.channels.Selector 所在的阻塞线程上的 interrupt() 方法也导致线程被唤醒。

 

Database Connection中查询的中断

违规代码示例(Database Connection)

该不合规示例展示了一个线程安全的DBConnector类,其每个线程创建一个JDBC连接。每一个连接属于一个线程,不会与其他线程共享。这是一个常见的用例,因为JDBC连接有意设计为单线程。

 

 

public class DataBaseConnection implements Runnable {

       private final String query;

 

       DataBaseConnection(String query) {

              this.query = query;

       }

 

       @Override

       public void run() {

              Connection connection;

              try {

                     connection = DriverManager.getConnection("jdbc:driver:name", "username", "password");

                     Statement stmt = connection.createStatement();

                     ResultSet rs = stmt.executeQuery(query);

              } catch (Exception e) {

                     // TODO: handle exception

              }

 

       }

 

       public static void main(String[] args) throws InterruptedException {

              DataBaseConnection connector = new DataBaseConnection("suitable query");

              Thread thread = new Thread(connector);

              thread.start();

              thread.sleep(5000);

              thread.interrupt();

 

       }

 

}

数据库连接像socket,没有内在的可中断性。因此,该设计无法实现当相应的线程阻塞在一个慢查询上时,客户端通过关闭资源来尝试取消任务, 如一个join操作。

 

合规解决方案(Statement.cancel())

该合规解决方案使用ThreadLocal 包装连接,线程调用initialValue()方法获取一个唯一的连接实例。这种方法可以提供一个cancelStatement(),以便其他线程或客户端可以在需要时中断慢查询。 cancelStatement() 方法调用 Statement.cancel()方法实现。

 

public class DBConnectorRight implements Runnable{

      

        final String query;

       private volatile Statement stmt;

       DBConnectorRight(String query){

              this.query=query;

       }

       private static final ThreadLocal<Connection> connectionHolder=

                     new ThreadLocal<Connection>(){

              Connection connection=null;

              @Override

              public Connection initialValue(){

                     try {

                    connection=DriverManager.getConnection("jdbc:driver:name","username","password");

                     } catch (SQLException e) {

                            // Forward to handler

                     }

                     return connection;

              }

             

       };

       public Connection getConnection(){

              return connectionHolder.get();

             

       }

 

       public boolean cancelStatement(){

              Statement tmpStmt=stmt;

              if(tmpStmt!=null){

                     try {

                            tmpStmt.cancel();

                            return true;

                     } catch (Exception e) {

                            // Forword to handler

                     }

              }

              return false;

       }

       @Override

       public void run() {

              try {

                     if(getConnection()!=null){

                            stmt=getConnection().createStatement();

                     }

                     if(stmt==null||(stmt.getConnection()!=getConnection())){

                            throw new IllegalStateException();

                     }

                     ResultSet rs=stmt.executeQuery(query);

              } catch (Exception e) {

                     // forward to handler

              }

             

             

       }

       public static void main(String[] args) throws InterruptedException {

              DBConnectorRight connector=new DBConnectorRight("suitable query");

              Thread thread=new Thread(connector);

              thread.start();

              thread.sleep(4000);

              connector.cancelStatement();

       }

 

}

Statement.cancel() 方法取消查询需要数据库和驱动两者都支持(译者注:比如mysql通过发送KILL QUERY 命令进行取消)。如果数据库或驱动不支持取消,则取消查询是不可能的。

 

根据Java API,Statement接口文档:

默认情况下,每个Statement同一时刻尽可以打开一个ResultSet对象。因此,如果一个ResultSet对象上的读取与另一个上的读取是交替的,那么每个ResultSet对象必须由不同的Statement对象生成。

 

该合规解决方案可以确保只有一个与Statement关联的ResultSet属于一个实例,且只有一个线程能访问到查询结果。

 

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

共有 人打赏支持
writeademo
粉丝 23
博文 462
码字总数 172216
作品 0
东城
多线程编程读书笔记之线程中断的本质

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

刘学炜
2012/07/03
0
0
读书笔记之《Java并发编程的艺术》-并发编程基础

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
0
8
java远程调试与JVM调优工具

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

chape
2013/12/24
0
0
Spring DataSource

何为DataSource DataSource 接口是 JDBC 2.0 API 中的新增内容,它提供了连接到数据源的另一种方法。 作为 工具的替代项, 对象是获取连接的首选方法。 实现 DataSource 接口的对象通常在基于...

冰雷卡尔
2012/06/04
0
1
Java并发编程笔记之基础总结(二)

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

狂小白
前天
0
0
java并发编程(六): 取消与关闭

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

ihaolin
2014/03/30
0
0
如何在云上安全高效地存放您的配置 - 代码示例

概述 在之前文章 如何在阿里云上安全的存放您的配置 - 续 中,我们讲述了云上安全存放配置的方法。基于前文原理,本文将以代码示例方式讲解如何基于配置中心产品 ACM 将应用配置(如 数据库连...

中间件小哥
06/01
0
0
spring下配置dbcp,c3p0,proxool

不管通过何种持久化技术,都必须通过数据连接访问数据库,在Spring中,数据连接是通过数据源获得的。在以往的应用中,数据源一般 是Web应用服务器提供的。在Spring中,你不但可以通过JNDI获取...

Hacken_Hu
2013/01/16
0
0
Java多线程之interrupt()的深度研究

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

恶魔在江湖
2014/02/18
0
0
Java 50道Java线程面试题

不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题。Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员的欢迎。大多数待遇丰厚的Java开发职位都要求开发者...

swearyd457
2015/08/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

about git flow

  昨天元芳做了git分支管理规范的分享,为了拓展大家关于git分支的认知,这里我特意再分享这两个关于git flow的链接,大家可以看一下。 Git 工作流程 Git分支管理策略   git flow本质上是...

qwfys
今天
1
0
Linux系统日志文件

/var/log/messages linux系统总日志 /etc/logrotate.conf 日志切割配置文件 参考https://my.oschina.net/u/2000675/blog/908189 dmesg命令 dmesg’命令显示linux内核的环形缓冲区信息,我们可...

chencheng-linux
今天
1
0
MacOS下给树莓派安装Raspbian系统

下载镜像 前往 树莓派官网 下载镜像。 点击 最新版Raspbian 下载最新版镜像。 下载后请,通过 访达 双击解压,或通过 unzip 命令解压。 检查下载的文件 ls -lh -rw-r--r-- 1 dingdayu s...

dingdayu
今天
0
0
spring boot使用通用mapper(tk.mapper) ,id自增和回显等问题

最近项目使用到tk.mapper设置id自增,数据库是mysql。在使用通用mapper主键生成过程中有一些问题,在总结一下。 1、UUID生成方式-字符串主键 在主键上增加注解 @Id @GeneratedValue...

北岩
今天
2
0
告警系统邮件引擎、运行告警系统

告警系统邮件引擎 cd mail vim mail.py #!/usr/bin/env python#-*- coding: UTF-8 -*-import os,sysreload(sys)sys.setdefaultencoding('utf8')import getoptimport smtplibfr......

Zhouliang6
今天
0
0
Java工具类—随机数

Java中常用的生成随机数有Math.random()方法及java.util.Random类.但他们生成的随机数都是伪随机的. Math.radom()方法 在jdk1.8的Math类中可以看到,Math.random()方法实际上就是调用Random类...

PrivateO2
今天
1
0
关于java内存模型、并发编程的好文

Java并发编程:volatile关键字解析    volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在...

DannyCoder
昨天
0
0
dubbo @Reference retries 重试次数 一个坑

在代码一中设置 成retries=0,也就是调用超时不用重试,结果DEBUG的时候总是重试,不是0吗,0就不用重试啊。为什么还是调用了多次呢? 结果在网上看到 这篇文章才明白 https://www.cnblogs....

奋斗的小牛
昨天
2
0
数据结构与算法3

要抓紧喽~~~~~~~放羊的孩纸回来喽 LowArray类和LowArrayApp类 程序将一个普通的Java数组封装在LowArray类中。类中的数组隐藏了起来,它是私有的,所以只有类自己的方法才能访问他。 LowArray...

沉迷于编程的小菜菜
昨天
0
0
spring boot应用测试框架介绍

一、spring boot应用测试存在的问题 官方提供的测试框架spring-boot-test-starter,虽然提供了很多功能(junit、spring test、assertj、hamcrest、mockito、jsonassert、jsonpath),但是在数...

yangjianzhou
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部