锁定老帖子 主题:Socket、Thread的使用记录
精华帖 (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已经越来越复杂越来越受软件界关注。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
浏览 4521 次