注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

0与1构筑世界,程序员创造时代

软件架构设计 Java编程

 
 
 

日志

 
 

Java网络处理模型-阻塞I/O+线程池  

2013-10-24 21:24:46|  分类: Java |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
"阻塞I/O+线程池"网络模型是"阻塞I/O+多线程"网络模型的改进版,人们意识到每次有新连接创建和销毁线程的代价太大,就用线程池来解决这个问题:每次接收到新连接后从池中取一个空闲线程进行处理,处理完成后再放回池中,重用线程避免了频率地创建和销毁线程带来的开销。

Java网络处理模型-阻塞I/O+线程池 - 傲风 - 宝剑锋从磨砺出 梅花香自苦寒来
  
"阻塞I/O+多线程"相比,在大量短连接的场景中性能会有提升,因为不用每次都创建和销毁线程,而是重用连接池中的线程。但在大量长连接的场景中,因为线程被连接长期占用,不需要频繁地创建和销毁线程,因而没有什么优势。

代码示例

ThreadPoolEchoServer.java 源代码:
package cn.aofeng.demo.io;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 线程池网络echo服务。每接收到一个新连接都由线程池中的空闲线程处理,连接关闭后释放线程(不会销毁线程,仍在线程池中)。
 
 @author <a href="mailto:aofengblog@163.com">NieYong</a>
 */
public class ThreadPoolEchoServer {

    private final static Logger logger = Logger.getLogger(MultiThreadEchoServer.class.getName());
    
    /**
     @param args [0]-监听端口
     */
    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("无效的参数。使用示例:");
            System.err.println("    java cn.aofeng.demo.io.ThreadPoolEchoServer 9090");
            System.exit(-1);
        }
        
        int port = Integer.parseInt(args[0]);
        ExecutorService threadpool = Executors.newFixedThreadPool(5);
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket();
            serverSocket.bind(new InetSocketAddress(port));
            if (logger.isLoggable(Level.INFO)) {
                logger.info("线程池网络echo服务启动完毕,监听端口:" +port);
            }
            while (true) {
                // 接收新的客户端连接
                Socket socket = serverSocket.accept();
                if (logger.isLoggable(Level.INFO)) {
                    logger.info("收到一个新的连接,客户端IP:"+socket.getInetAddress().getHostAddress()+",客户端Port:"+socket.getPort());
                }
                
                // 将Socket连接交给线程池处理
                threadpool.submit(new Worker(socket));
            }
        catch (IOException e) {
            logger.log(Level.SEVERE, "处理网络连接出错", e);
        }
    }

}

Worker.java 源代码:
package cn.aofeng.demo.io;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 处理客户端Socket连接的工作线程。
 
 @author NieYong <aofengblog@163.com>
 */
public class Worker implements Runnable {

    private final static Logger logger = Logger.getLogger(MultiThreadEchoServer.class.getName());
    
    // 字符集编码
    private final static String CHAR_SET = "utf8"
    
    // 行结束符
    private final static String CRLF = "\r\n";
    
    private Socket socket = null;
    
    public Worker(Socket socket) {
        this.socket = socket;
    }
    
    public void setSocket(Socket socket) {
        this.socket = socket;
    }
    
    public void close(Closeable c) {
        if (null != c) {
            try {
                c.close();
            catch (IOException e) {
                // ingore
            }
        }
    }
    
    @Override
    public void run() {
        if (null == socket || socket.isClosed()) {
            logger.warning("无效的Socket连接:" + socket);
            return;
        }
        
        String lineEnd = CRLF;
        try {
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(
                            socket.getInputStream()));
            OutputStream outs = socket.getOutputStream()
            String line = null;
            while null != (line = reader.readLine()) ) {
                // 客户端退出
                if ("quit".equalsIgnoreCase(line|| "exit".equalsIgnoreCase(line)) {
                    break;
                }
                
                outs.write(line.getBytes(CHAR_SET));
                outs.write(lineEnd.getBytes(CHAR_SET));
            }
            close(reader);
            close(outs);
        catch (IOException e) {
            logger.log(Level.SEVERE, "读取网络连接数据出错", e);
        }
    }
    
}

验证

1、启动服务。
java cn.aofeng.demo.io.ThreadPoolEchoServer 9090
执行上面的命令,启动服务,输出信息:
2013-10-24 21:06:43 cn.aofeng.demo.io.ThreadPoolEchoServer main
信息: 线程池网络echo服务启动完毕,监听端口:9090

2、打开三个终端窗口,执行命令:
telnet 192.168.56.102 9090
服务输出如下信息:
2013-10-24 21:11:05 cn.aofeng.demo.io.ThreadPoolEchoServer main
信息: 收到一个新的连接,客户端IP:192.168.56.101,客户端Port:1167
2013-10-24 21:11:10 cn.aofeng.demo.io.ThreadPoolEchoServer main
信息: 收到一个新的连接,客户端IP:192.168.56.101,客户端Port:1170
2013-10-24 21:11:15 cn.aofeng.demo.io.ThreadPoolEchoServer main
信息: 收到一个新的连接,客户端IP:192.168.56.101,客户端Port:1171
注:服务所在机器的IP地址是192.168.56.102。

3、连接一段时间后,从终端输入exit或quit指令,服务端关闭连接,对应的线程也随之销毁,如下图所示:
Java网络处理模型-阻塞I/O+线程池 - 傲风 - 宝剑锋从磨砺出 梅花香自苦寒来
 可以看到连接池创建了三个线程(pool-1-thread,pool-2-threadpool-3-thread),当连接关闭后,线程并没有销毁,只是从运行状态变成空闲状态。

<正文结束>
文章声明


作者:傲风(aofengblog@163.com)       编写时间:2013年10月24日

网址:http://aofengblog.blog.163.com

作者保留所有权利,转载请保留文章全部内容或者说明原作者和转载地址!

  评论这张
 
阅读(1288)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017