论坛首页 Java企业应用论坛

Socket、Thread的使用记录

浏览 4522 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-04-03  

做了太多时候的Web项目,Socket这么基础而又重要的接口都快忘掉了,虽然偶尔做做,但是都不大深入,刚好前段时间又做了点Socket类的应用,又对Socket接口有一些新的认识,先记下来

 

放在blog上马上就有人指出了我的错误,特此感谢,所以又拿到论坛上来等着大家拍,如有错误,欢迎指正 

 

1.SocketStream截断字符

在FileStream里面最后一位截断字符是-1,这样在操作文件的时候就可以使用如下的判断:

BufferedReader buffer = new BufferedReader(new InputStreamReader(socket
		 .getInputStream(), "UTF-8"));
DataInputStream in = new DataInputStream(connection.getInputStream());
while ((count = in.read(buffer)) > 0) {
        out.write(buffer, 0, count);
}

而这招在Socket里面是不灵的,因为如果这个判断要成立,需要对方Socket主动关闭,客户端收到EOF以后才能使得这个判断成立,否则对方不关,count永远得不到-1,就会一直阻塞在那里。
 

因为Socket接口能互相传送的东西是双方协议规定好的,什么字符都可能出现,这样只用-1来做截断判断是不对的,如果双方认定-1或者某些特殊字符可以作为截断标识那也可以,这样做只有当已知传递文件不会出现-1的时候才行,这种判断的习惯可能是来自于C语言,但是C语言是有一个EOF,而Java里面没有提供。

解决这个问题的办法,经过朋友指点变成了这样:


int BS=100,count=0;
byte[]buffer=new byte[BS];
int has_readed=0;
DataInputStream in = new DataInputStream(connection.getInputStream());  
while ((count = in.read(buffer)) > 0 && has_readed < BS) {  
     int left=BS-has_readed;
     out.write(buffer, 0, count>left?left:count);  
     has_readed+=count;
    }

 上面的代码变得复杂了许多,思路是这样的:

A.每次读取都是读指定长度的byte流,这样在协议里面写好长度,读取指定的内容,读取精确,无需阻塞,效率也高。

B.采用一个has_readed作为计数器,防止网络阻塞的情况下不能读到数据,增强程序的健壮性。

 

2.count = in.read(buffer)

这个东西看起来再普通不过,但以前没有仔细去想,也没有什么资料去分析过,这里让我们仔细看看,C语言里面允许一个方法返回两个参数,但是Java里面是非法的,然后这个in.read(byte b[])这个方法也做了类似的事情:

A.为read方法读到的buffer的个数,如果读不到值会返回-1。

B.read方法将读到的东西附给了buffer这个byte数组,这样就达到了返回两个参数的效果,因为数组是Object对象,这样将它作为参数传进去就可以将其赋值,基本类型是做不到这点,只有对象可以(大家用的多的是List,Map之类)。

 

3.byte还是String

最早接触Java的时候做Socket,就是用String类型来传输,很方便,双方接口处理起来都很简单而且更直观,这次接口传输的却是byte[],搞的我很郁闷,可能是我的Java基础并不够扎实(后来同事给的评语是你肯定是没怎么写过C……),byte[]就不爽了,就是一大堆莫名其妙的数字还要对各种类型做转化控制数位什么的,稍不小心就会出错。起初一直以为是客户那边有技术上的风格习惯,后来想想,这Socket可是语言之间的沟通桥梁,又不是单为你Java做的,像C语言这种没给你准备String这个类型啊。其实String类型还好,怎么说还能变通一下实现,有人说在Socket里面传递序列化的对象可就更麻烦拉。用byte[]数组对类型转换啥的也能更熟悉一些。

 

4.使用Excutor来代替Thread(内容源自Java Concurrency in Practice,如果需要找到详细内容,请参看原书第六章)

一道经典的面试题:Java有哪几种方式来实现线程?答:继承Thread和实现Runnable接口,但是1.5以后就不能这么回答,应该再加上java.util.concurrent,套用《Java并发编程实践》中的一句话:

无论何时当你看到这种形式的代码:

    new Thread(runnable).start()

    并且你可能最终希望获得一个更加灵活的执行策略时,请认真考虑使用Executor代替Thread

 在使用Socket的时候我们经常会这样写:

ServerSocket socket = new ServerSocket(7001);
While(true){
     final Socket connection = socket.accept();
    Runnable task = new Runnable(){
              public void run(){
                      //hanld reuqust code
              }
    };
    new Thread(task).start();
}
 这看上去似乎没问题,而且所有的教科书里面都这样写的,一个请求一个线程,无限制创建线程,在原型和产品开发的时候都表现良好,但是这种每任务每线程(thread-per-task) 方法存在实际的缺陷,特别是需要创建大量线程的时候会更加突出:

A.线程生命周期的开销

B.资源消耗量

C.稳定性

具体描述可以参看原书,所出现的问题是很容易理解的,所以即便你的程序还在使用1.4版本(Third party),如果大量依赖线程,特别是需要处理大量并发的情况下,应该采用java.util.concurrent提供的线程池,concurrent相对Thread更加稳定且功能丰富,可以使多线程并发程序有更为稳定的表现。

我们原来的接口程序就是采用原始的Thread,并发量要求达到500个,使我异常的头痛,那么如果采用Concurrent线程池的就能获得更好的并发性能并确保服务器的稳定性(可惜暂时没有机会实践)。 

 

5.期待JDK7

JDK7现在正在开发阶段,目前的趋势是SUN将为JDK7添加对多核开发提供更好的支持,那么主要就是体现在java.util.concurrent上面,我目前正在阅读《Java并发编程实践》,争取有时间写个读书笔记什么的,要对并发开发保持持续的跟进学习。

前几天看到JavaEye新闻里面有一个人写的有关JDK7对多核开发的支持的疑问,也值得我们去思考一下:

第一点是java不能仅仅是提供支持线程开发包这么简单,而应该是像Scala、Erlang这种在语言级别就支持多核

第二点是面对即将到来的多核时代,如何利用好8核、16核这种处理器不仅仅是如何更好的发挥CPU的性能,因为即便你能发挥多核的潜力,但是你将不得不面对大量I/O操作的处理,I/O处理将会成为更为难缠的瓶颈,因为这种I/O资源是只能使用锁策略。

貌似这些东西还挺远,但是很有趣,还有一些Java想加入的语法糖,和对动态语言支持的大量的争论,真不知道JDK7会是个什么样子,总之Java已经越来越复杂越来越受软件界关注。

 

论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics