`

如果你用单线程写Socket,为什么要折腾?--单线程、多线程、线程池

阅读更多
在开发Socket项目的时候,如果是开发一个自己玩玩,当然不用考虑效率、安全性等问题,可是如果是一个企业级的,你就不得不关注这几点。本系列文章将我们实验室里的Socket程序变成企业级的应用。
NIO编程肯定是一个很好的解决方案,不过这部分留在以后讨论。今天我想说说如何让你的阻塞的Scoket程序高效、安全的跑起来。
一开始,大家会编出一个单线程的Scoket程序,然后我们发现这个程序根本不能够连接多个客户端,于是我们引入“多线程”,使我们的程序能够同时处理多个客户端。
我相信,到现在为止如果没有深入研究过Socket编程,大家一般还是停留在“一客户一线程”的初级模式。如果是个位数的客户,当然你不会发现什么明显的性能问题。但是如果你的客户连接数量达到百位级,我靠,你的CPU就关顾着在各个线程间切换,你的内存似乎也有些吃不消了(每个线程都有自己独立的内存),更多的系统资源的消耗,更多的线程上下文转换,更复杂的线程管理(OS有一套自己的机制),将拖垮你的application。再加上多客户端尝试并发连接,及时响应客户端的连接将变得像癞蛤蟆追求天鹅一样不给力,因为线程的创建将占用服务器大量的CPU周期。
单线程多线程我们没法解决的问题,必然有新的英雄站出来解决,他就是“线程池”。
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 * ThreadPool演示
 * @project : socket
 * @author  贾懂凯 @ netjava 
 * @date  2010-12-14 下午12:24:40
 * @since jdk1.6.0_10
 */
public class TCPServerPool {
	
	public static void main(String args[]) throws IOException{
		//ensure the parameter is right!
		if(args.length!=2){
			throw new IllegalArgumentException("Parameter(s):<port> <ThreadSize>");
		}
		int server_port=Integer.parseInt(args[0]);
		int threadpool_size=Integer.parseInt(args[1]);
		
		//create a server socket to accept client connection requests
		final ServerSocket serSock=new ServerSocket(server_port);
		final Logger logger=Logger.getLogger("thredPoolLog");
		
		//spawn a fixed number of threds to service clients
		for(int i=0;i<threadpool_size;i++){
			Runnable run=new Runnable(){

				public void run() {
					while(true){
						try {
							Socket clientSock=serSock.accept();
							/**
							 * 把它交给一个独立的handler处理
							 * 你可以将handler定义为一个独立的线程(注意性能)
							 * 或者定义为一个静态方法(注意并发的同步问题)。
							 */
						} catch (IOException e) {
							logger.log(Level.WARNING,"Client accept failed",e);
						}
					}
				}
			};
			Thread t=new Thread(run,"Thread-"+i);
			t.start();
			logger.info("create and start a thread named "+t.getName());
		}
	}
	
}

     这里我为accept()方法加上了线程池,我们发现,我们不必担心少量的多个客户并发连接的问题了,因为有多个线程对应的accept()在等待客户端连接进来。一旦客户成功连接进来,该线程重新返回线程池。如果并发访问的客户端超过线程池的size,那么连接请求将在网络中形成一个队列等待,这明显是不利于维护的,并且线程的大小没有适应性,因为它总是一成不变的。
    并且连接进来的客户端显然不能交给单独的线程来处理,否则我们控制线程数量过多造成的性能瓶颈的初衷将无疾而终。不过,如果给连接进来的客户端创建一个线程池,就要考虑到维护问题,我们创建一个等待队列,来维护那些超过客户端线程池size的线程。这样将引起一致命的问题,如果其中有几个正在接受服务的线程阻塞等待或者由于未捕获异常死亡,在等待队列中的线程将因迟迟得不到资源而被饿死。
     线程池的初步使用出现了这么多问题,问题总是伴随着被解决的可能性诞生的,就像出现了怪兽总会出现奥特曼一样,于是我们的英雄又出现了-“Executor”,它是系统提供的,它可以帮助我们来管理线程池。
如何管理,待我吃完饭回来继续……(见下一篇)
分享到:
评论
28 楼 killeraction 2011-05-11  
快去学习用netty或者是mima把? 楼主你Out了..
27 楼 贾懂凯 2010-12-18  
pengzhoushuo 写道
LZ,你有没有考虑到一个问题呢,比如说游戏服务器或者IM服务器,要保持长连接吧,你想多个线程来处理很多的Socket,而每个Socket里的都有wile(true)呢。

阻塞编程中,如果是大量常量的话用线程池显然不是一个号的选择。线程池主要是服务于大量的短连接的系统,可以节约创建和销毁线程的系统消耗。
如果是有大量长连接,用NIO的selector监听会比较好。我也没做过游戏服务器,我暂时是这么考虑的。
还是throw new MyExcpetion(this question),求大家解答吧~~
26 楼 pengzhoushuo 2010-12-18  
LZ,你有没有考虑到一个问题呢,比如说游戏服务器或者IM服务器,要保持长连接吧,你想多个线程来处理很多的Socket,而每个Socket里的都有wile(true)呢。
25 楼 skzr.org 2010-12-18  
mercyblitz 写道
贾懂凯 写道
skzr.org 写道
真的是折腾,直接使用NIO.2
呵呵,很简单就完成了

就是要折腾嘛!呵呵~
好久没在JE上看见你了,最近挺忙的?



他看Oracle Java大会去了,:-)!

最近半年忙着做工程,没怎么开发,郁闷中。。。

javaone确实不需此行,比csdn的好多了

折腾好啊,可以更加的深入理解
折腾中会引入更多的问题和要解决的东西,就像

C_J 写道
其实你还有更多的问题要考虑:
1,池中没有可用线程,怎么办?
2,任务队列满了,怎么办?
3,超时机制。


期待凯哥此文的综合帖
持续关注 中。。。
^ ^
24 楼 C_J 2010-12-18  
其实你还有更多的问题要考虑:
1,池中没有可用线程,怎么办?
2,任务队列满了,怎么办?
3,超时机制。
23 楼 mercyblitz 2010-12-17  
yeshucheng 写道
借这个帖子,很想了解下这里有朋友去看activeMQ的源代码吗,呵呵。很多帖子都是写了一些如何运用activeMQ,并没有深入了解它的原理



先了解JMS就淡定了,然后才是传输协议的细节。
22 楼 yeshucheng 2010-12-17  
借这个帖子,很想了解下这里有朋友去看activeMQ的源代码吗,呵呵。很多帖子都是写了一些如何运用activeMQ,并没有深入了解它的原理
21 楼 mercyblitz 2010-12-17  
贾懂凯 写道
skzr.org 写道
真的是折腾,直接使用NIO.2
呵呵,很简单就完成了

就是要折腾嘛!呵呵~
好久没在JE上看见你了,最近挺忙的?



他看Oracle Java大会去了,:-)!
20 楼 贾懂凯 2010-12-17  
skzr.org 写道
真的是折腾,直接使用NIO.2
呵呵,很简单就完成了

就是要折腾嘛!呵呵~
好久没在JE上看见你了,最近挺忙的?
19 楼 skzr.org 2010-12-17  
真的是折腾,直接使用NIO.2
呵呵,很简单就完成了
18 楼 mercyblitz 2010-12-16  
贾懂凯 写道
mercyblitz 写道
贾懂凯 写道
taolei0628 写道
用Executor不见得就比你现在的方法好。
等你贴出线程池的用法后再看看有没有我预计到的问题出现吧。
给你个提示,网络连接是个比线程更重量级的资源。

确实如此,三次握手会占比较长时间,只有到第三次握手完成才会返回socket实例。我觉得如果并发访问要求比较高的话,用我上面的代码中的线程池方法应该能增加效率。不过,如果在accept方法上加一个超时机制效果会更好。
求taolei0628指点~~



TCP/IP上面还有UDP,你的例子是TCP而已。资源消耗大,更大的程度是因为走IO总线。

是netjava小马哥吧,我是学员。我知道做通信我还有很长的路要走的,现在刚开始……



是的,没有关系,慢慢来啊,你会stronger的。

相互学习哦,呵呵!看看我给你的留言,你可以考虑一下!
17 楼 taolei0628 2010-12-16  
在多数java应用里用TCP还是比较靠谱的,用UDP可能要解决的问题TCP都已经帮我们解决了。
我说到的问题其实是我看到的一段基于线程池的Socket服务器端框架代码,不知道是不是普遍存在。
在那段代码中先accept,此时连接已经建立,再把它放进任务队列等待处理。
通常这个等待是有并发和等待超时限制的,我认为正确的方式是在真正能提供服务的时候再accept。
楼主的非线程池的代码正好回避了这个问题,所以我才说了上面的话。
16 楼 贾懂凯 2010-12-16  
mercyblitz 写道
贾懂凯 写道
taolei0628 写道
用Executor不见得就比你现在的方法好。
等你贴出线程池的用法后再看看有没有我预计到的问题出现吧。
给你个提示,网络连接是个比线程更重量级的资源。

确实如此,三次握手会占比较长时间,只有到第三次握手完成才会返回socket实例。我觉得如果并发访问要求比较高的话,用我上面的代码中的线程池方法应该能增加效率。不过,如果在accept方法上加一个超时机制效果会更好。
求taolei0628指点~~



TCP/IP上面还有UDP,你的例子是TCP而已。资源消耗大,更大的程度是因为走IO总线。

是netjava小马哥吧,我是学员。我知道做通信我还有很长的路要走的,现在刚开始……
15 楼 yeshucheng 2010-12-16  
LZ最好把你的认识深入浅出写出,这样挺好的。学习,呵呵
14 楼 贾懂凯 2010-12-16  
yeshucheng 写道
accept:内核数+1
handler交给worker线程做

其实NIO很多都类似这样干的

个人觉得NIO相对于阻塞编程有以下优点:
1、节约了多客户阻塞等待的系统开销(用Selector成组监听)
2、一定程度上限制了同步问题的发生(如果你用独立的线程处理handler的话那就未必)
3、将阻塞编程中本来用stream屏蔽的套接字缓冲暴露给程序员,这样做的好处是程序员可以
根据不同的系统需求自己设计缓冲区(并且“直接缓冲区”的应用也很给力),能很好的在内存和CPU的开销见找到平衡点(这个很难,得有点功力了)。
4、当然,控制某些连接的优先级也不像阻塞编程中(仅仅通过设置线程优先级)那样不可预测了。
……
准备过一段时间专门写一篇,今天不多说了。
13 楼 贾懂凯 2010-12-16  
rainsilence 写道
吃完饭回来没下文了?

见:http://www.iteye.com/topic/842139?page=2#1806256
12 楼 rainsilence 2010-12-16  
吃完饭回来没下文了?
11 楼 mercyblitz 2010-12-16  
贾懂凯 写道
taolei0628 写道
用Executor不见得就比你现在的方法好。
等你贴出线程池的用法后再看看有没有我预计到的问题出现吧。
给你个提示,网络连接是个比线程更重量级的资源。

确实如此,三次握手会占比较长时间,只有到第三次握手完成才会返回socket实例。我觉得如果并发访问要求比较高的话,用我上面的代码中的线程池方法应该能增加效率。不过,如果在accept方法上加一个超时机制效果会更好。
求taolei0628指点~~



TCP/IP上面还有UDP,你的例子是TCP而已。资源消耗大,更大的程度是因为走IO总线。
10 楼 yeshucheng 2010-12-16  
accept:内核数+1
handler交给worker线程做

其实NIO很多都类似这样干的
9 楼 贾懂凯 2010-12-16  
zhaoxin1943 写道
贾懂凯 写道
taolei0628 写道
用Executor不见得就比你现在的方法好。
等你贴出线程池的用法后再看看有没有我预计到的问题出现吧。
给你个提示,网络连接是个比线程更重量级的资源。

确实如此,三次握手会占比较长时间,只有到第三次握手完成才会返回socket实例。我觉得如果并发访问要求比较高的话,用我上面的代码中的线程池方法应该能增加效率。不过,如果在accept方法上加一个超时机制效果会更好。
求taolei0628指点~~

楼主为何不考虑用现成的框架呢?我们项目也需要用到socket编程,前一阵子用过netty,后来发现XSocket更好用。

框架易用,底层难把握,况且需求各异要求设计应时而变。

相关推荐

    单线程与多线程socket通信

    首先,我们来详细探讨单线程Socket通信。在单线程模型中,服务器端只有一个线程处理所有客户端的连接请求。当一个客户端发起连接请求时,服务器端创建一个Socket实例,并通过这个Socket实例与客户端进行通信。这种...

    JAVA 写的SOCKET线程池

    Java中的Socket线程池是一种高效的网络编程模型,它结合了Socket通信和多线程技术,以提高服务端处理客户端请求的并发性能。对于初学者来说,理解并掌握这个概念至关重要,因为这能帮助他们构建更稳定、可扩展的网络...

    linux下多线程读写socket的简单例子

    在多线程读写Socket的应用中,我们可能会创建一个主线程负责监听和接收连接,然后为每个新的客户端连接创建一个单独的线程来处理读写操作,这样可以避免单线程模型下的阻塞问题,提高服务端的并发处理能力。...

    Java Socket学习---单线程阻塞

    "Java Socket学习---单线程阻塞"这个主题主要探讨了如何使用Java的Socket类进行单线程的阻塞式通信,这种模式通常用于简单的客户端-服务器应用。在这个场景中,服务器端通过一个线程处理一个客户端的请求,当请求被...

    serversocket单线程跟多线程例子

    本文将深入探讨`ServerSocket`在单线程和多线程环境下的应用,以及它们各自的特点和适用场景。 首先,我们来看`ServerSocket`的单线程例子。在单线程模式下,服务器只有一个线程来处理所有客户端的连接请求。这通常...

    socket编程 使用select与线程池

    线程池是一种线程使用模式,它预先创建了一组线程,当有任务需要执行时,线程池会分配一个空闲线程来执行任务,任务完成后,线程返回线程池等待下一次分配。这种方式提高了系统资源的利用率,减少了线程创建和销毁的...

    C++多线程SOCKET收发

    在C++编程中,多线程SOCKET收发是一项重要的技术,它允许程序同时处理多个网络连接,提高系统的并发性能。...通过学习和实践这个主题,你可以深入理解C++的多线程特性和网络编程,提升软件的性能和用户体验。

    socket服务器线程池的实现

    2. **多线程编程**:线程是程序执行的最小单元,C++中可以通过`#include &lt;thread&gt;`库来创建和管理线程。每个连接到服务器的客户端请求都可以作为一个独立的任务在新线程中处理,这样可以避免主线程阻塞,提高并发...

    多线程与socket网络编程-内附视频链接-新手入门资料

    在IT领域,多线程和Socket网络编程是两个至关重要的概念,它们对于开发高效、可靠的分布式系统至关重要。这里我们将深入探讨这两个主题。 1. **进程** - **进程的定义**:进程是操作系统中资源分配的基本单位,它...

    Windows Socket编程 多线程

    总的来说,Windows Socket编程与多线程的结合是构建高性能网络服务的核心技术之一,对于想要深入理解和开发网络应用的IT从业者来说,这是不可或缺的知识点。通过不断地学习和实践,开发者能够更好地理解和驾驭这一...

    异步和多线程socket通讯

    本文将深入探讨"异步和多线程socket通讯"这一主题,基于提供的描述和标签,我们将讨论如何利用多线程和异步机制来提升socket通信的效率和响应性。 首先,Socket是一种在应用程序与网络服务之间建立连接的接口,它...

    delphi多线程socket编程介绍

    ### Delphi多线程Socket编程介绍 随着分布式系统的广泛应用,多任务并发技术变得越来越重要。在当前基于多线程的操作系统环境下,开发并发多任务程序已成为软件开发领域的一个热点话题。Delphi作为一种强大的开发...

    单线程Socket编程实现mysql数据读取

    - 考虑到多线程,如果服务器需要处理多个客户端连接,可以使用线程池或并发框架。 - 数据安全,对敏感信息如数据库密码进行加密处理。 - 数据的序列化和反序列化要确保两端的兼容性。 以上就是使用单线程Socket编程...

    socket短连接和长连接 多线程的应用

    学习这个框架可以帮助理解如何在实际应用中有效地管理和使用Socket连接,以及如何通过多线程和线程池优化并发性能。通过阅读和分析源码,可以深入理解这些概念,并提升网络编程和并发处理的能力。

    java Socket 多线程

    Java Socket 多线程是网络编程中的一个重要概念,它结合了Java的并发处理能力和Socket通信技术,使得服务器能够同时处理多个客户端的连接请求。在Java中,Socket是用于在网络环境中进行双向通信的类,而多线程则允许...

    C# 多线程socket客户端

    本篇文章将深入探讨如何使用C#构建一个多线程的Socket客户端,特别是针对聊天室场景。 标题中的"C# 多线程socket客户端"指的是在C#环境中,利用Socket类和多线程技术来创建一个能够并发处理多个连接的客户端应用。...

    C# 多线程socket 实例

    C#作为一种功能强大的.NET编程语言,提供了丰富的库支持来实现网络通信,其中包括使用socket进行多线程编程。本篇文章将深入探讨如何在C#中利用多线程和socket技术创建一个聊天室实例。 首先,理解`socket`的概念至...

    delphi多线程socket服务器客户端

    通过上述步骤,你可以构建一个基础的Delphi多线程Socket服务器客户端系统,实现高效、稳定的网络通信。随着需求的增加,可以扩展功能,如添加多线程池管理、数据压缩、消息队列等高级特性,以提升系统的性能和可靠性...

    c#超级Socket库SuperSocket,SOCKET多线程编程

    综上所述,SuperSocket库为C#开发者提供了一个强大而灵活的Socket服务开发框架,通过多线程处理、易于扩展的API以及跨平台支持,使得构建Socket应用变得更加高效和便捷。通过学习和掌握SuperSocket,开发者可以快速...

    socket多线程通信

    Socket多线程通信是网络编程中的一个重要概念,它结合了Socket接口与多线程技术,以提高服务端处理客户端请求的效率。C/S(Client/Server)模型是网络通信的基本架构,其中C代表客户端,S代表服务器端。在这个模型中...

Global site tag (gtag.js) - Google Analytics