socket连接与多线程

  socket连接是Java中进行通信的基本方式,也是效率最高的方式,虽然他有http等让是进行http请求,但是如果是进行tcp、下载等通信,还是使用socket更好。Java中封装了非常完美的socket机制,使用也非常简单。主要包括socket和serversocket。

  socket的使用非常简单,主要包括的构造方法有:socket(),socket(string host,string port),socket(Inetaddress address,int port)等,非常明白了,通过传入host和port进行socket的请求,当在创建相应的套接字实例的时候,会自动去对相应的ip和port进行连接,只有当连接成功,才表示相应的套接字建立成功,才可以进行相应的I/O操作。通过getOutputStream和getInputStream获取相应的输入输出流,进行相应的I/O操作,但是有一个是比较特别的,getChannel用来获取SocketChannel,他之所以特别是因为他属于java.nio.channels下面的类,其继承于java.nio.channels.SelectableChannel,就是说在进行nio非阻塞式的请求连接时,他非常有用,具体参见http://www.cnblogs.com/likwo/archive/2010/06/29/1767814.html。可能有些人会问,对于可以通过设置服务器连接的timeout来防止过多的阻塞,但是如果对于超过timeout,socket一般是抛出超时异常,这样就算对异常进行了处理,也将会从新建立socket连接,浪费消费 重建的资源。例如QQ聊天,当你打开一个聊天面板,很久不说话的时候,并不会自动为你断开socket连接,而是一直处于阻塞状态,直到你发送了新的信息,再进行处理,因此nio的阻塞方式会更好些。

  对于serversocket也比较简单,常用的只有四个构造函数:

l  ServerSocket()throws IOException
l  ServerSocket(int port) throws IOException
l  ServerSocket(int port, int backlog) throws IOException
l  ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException  

分别对这几个构造进行简单的解释:

第一个无参构造,只是创建一个serversocket实例,但是不进行任何端口的监听,你还必须通过bind方法进行端口的绑定,好处就是在绑定之前可以进行相应属性的设置,例如so_reuseaddress等;

第二个构造函数需要一个端口(1024-65535),一般不使用0-1023之间的,这个属于系统占用的预留端口,但是如果你传入的端口号为0,则会默认使用匿名端口,就是系统随机分配一个端口,进行暂时通信,这个匿名方式,一般情况下不使用;

第三个构造函数需要一个端口,和一个backlog(监听对列大小),serversocket进行某端口的监听,当有多个连接请求是,每个请求默认都会放入一个请求队列里,如果你没有设置这个值,则默认为操作系统的值,根据不同的系统有所不同,例如40等,有几种情况,这个值将会失效:大于操作系统默认值|小于等于0|没有设置。如果设置了,而没有及时对队列进行处理,则会报ConnectException异常;

第四个构造函数除了具有端口、队列大小外,还具有一个参数是ip地址,就是进行相应ip地址的绑定。当然,这个进行的是服务器ip地址的绑定,不会限制客户端的ip访问。当一台服务器存在多个网卡的时候,就需要通过这个参数来设置客户端访问的ip。

服务器socket的关闭,通过使用close进行关闭,使用isclosed进行判断,还可以进行端口的绑定判断。

对于服务器close方法,需要有一点进行说明:调用close方法之后,操作系统并不会立即进行端口的释放,依旧会对旧端口占用一段时间,以防止客户端发送的数据有延迟现象。因此有时候,就算你进行了close方法的调用,进行了端口的释放,但是如果你立即进行同一个端口的连接时,依旧会包端口占用异常,这个是可以理解的。

serversocket通过使用accept方法进行客户端请求的处理,每当请求队列里有客户端请求时,serversocket就会从队列顶端取一个socket请求进行处理,生成一个socket来负责与客户端通信。如果一个时间只能处理一个socket,当有多个客户端请求时,则必须要排队处理,等待所有前面的socket处理完,这是个极其痛苦,并且不合理的过程。因此,引入了线程的概念。

  为了实现客户端请求的快速相应和快速处理,据是高并发,则必须使用多线程机制。主题思想是:serversocket通过accept建立一个socket,然后起一个现成,把这个socket扔给新建的线程进行处理,而serversocket主线程,则继续去监听端口,以此实现多线程通信。一般有三种方式:

  1、每一个socket请求就建立一个线程。这个是最简单的方式,大致代码如下:

  • public void service() {   
  •   while (true) {   
  •     Socket socket=null;   
  •     try {   
  •       socket = serverSocket.accept();                                            //接收客户连接   
  •       Thread workThread=new Thread(new Handler(socket));            //创建一个工作线程   
  •       workThread.start();                                                            //启动工作线程   
  •     }catch (IOException e) {   
  •        e.printStackTrace();   
  •     }   
  •   }   

  上面的方式非常简单,能够处理基本的多线程问题,当数据量不大时,应该没有什么问题,但是如果数据量过大时,就会出现严重的性能,甚至是宕机问题。其缺点主要有如下几个:

  a:每个socket请求,建立一个连接,当每个都是进行简短的通信时,则异常的耗费系统建立、销毁线程资源。

  b:如果建立线程太多,每个线程都会占用一定的系统内存,这样将导致内存溢出。

  c:频繁地对线程进行建立 销毁,会导致操作系统进行频繁的cpu切换线程切换,这样也会非常耗费系统资源。

2、自己实现线程池。

  自己写线程池,能够对线程池的工作原理以及工作情况,更加的了解和控制,但是由于线程池必然涉及到多线程问题,因此为了防止出现死锁、线程泄漏、并发错误、任务过载等问题,需要性能非常好的机制,一般不推荐个人现实。如果非要实现,可以通过使用linkedlist<runnable>的数据结构来实现一个多线程队列。下面,还是主要推荐jdk已经帮你实现的线程池。

3、使用jdk自带的线程池。jar包是:java.util.concurrent

  这个jar包都是一些并发编程会经常使用到的工具类,主要有阻塞队列,原子操作的map以及线程池等。其基本包括Executor、ExecutorService接口和Executors类,两个接口定义了执行线程的方法,而Executors则定义了管理线程池的方法,主要可以创建的常用线程池有:

newSingleThreadScheduledExecutor() 创建一个可以延迟执行和定时执行的单线程线程池

newSingleThreadExecutor() 创建一个运行单线程的线程池

newScheduledThreadPool(int corePoolSize)  创建一个可以延迟执行和定时执行的线程池,设定线程数

newFixedThreadPool(int nThreads)  创建一个固定线程数的线程池

newCachedThreadPool() 创建一个带有缓冲区的线程池

然后通过生成的线程池的execute方法进行线程的执行,具体可以百度下哈哈

使用线程池有以下几点风险:

1、死锁。任何多线程都不可避免的问题。但是对于线程池可能会存在另一种死锁:就是线程池中的线程都在等待一个资源,而这个资源需要执行A后得到,而由于线程池没有可用的线程,导致A无法执行,故而也会发生死锁。

2、系统资源不足。多线程必定需要大量的内存资源,可能出现内存泄漏问题。

3、并发错误。

4、线程泄漏。就是所有的线程池中的线程都在等待输入资源,或者都抛出了异常而没有捕获,则会导致线程池中所有的线程假死。

5、线程过载。运行线程过多,导致过载,这个可以通过设置线程池的大小来进行一定成功的避免。

至于如何避免,主要是要在使用多线程时要小心,同时不要使用destroy despause 等操作,尽量使用sleep notify wait等操作,在这里不详细说明了。

转载于:https://www.cnblogs.com/canghaitianyuan/archive/2012/11/16/2772987.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值