﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Todd-随笔分类-java</title><link>http://www.blogjava.net/Todd/category/44626.html</link><description>记住‘被遗忘者’的含义，我们既非生者也非死者，我们将被活着的和死去的人遗忘。&lt;br /&gt;我们回到了曾经告别的世界上，但是却永远无法回到我们曾经活着的那些日子，&lt;br /&gt;永远无法回到那些我们曾经爱过的人的身边。我们是存在也是诅咒，&lt;br /&gt;因此我们遗忘过去，并且被过去遗忘... </description><language>zh-cn</language><lastBuildDate>Wed, 19 Oct 2011 02:09:56 GMT</lastBuildDate><pubDate>Wed, 19 Oct 2011 02:09:56 GMT</pubDate><ttl>60</ttl><item><title>java heap space， PermGen space 错误 使用jvisualvm监测设置合理值</title><link>http://www.blogjava.net/Todd/archive/2011/10/18/361543.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Tue, 18 Oct 2011 14:16:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2011/10/18/361543.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/361543.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2011/10/18/361543.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/361543.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/361543.html</trackback:ping><description><![CDATA[使用myeclipse启动tomcat 报java heap space ，PermGen space 错误，分别为 heap内存不足，PermGen内存不足<br />需加大 tomcat启动项参数 Xmx 和&nbsp;XX:MaxPermSize<br />PermGen是指内存的永久保存区域，它用于存放class和 method 对象，以及String 对象<br />（sun原文：permanent generation is the area of the heap where class and method objects are stored. If an application loads a very large number of classes, then the size of the permanent generation might need to be increased using the -XX:MaxPermSize option. 
<div>Interned java.lang.String objects are also stored in the permanent generation. The java.lang.String class maintains a pool of strings. When the intern method is invoked, the method checks the pool to see if an equal string is already in the pool. If there is, then the intern method returns it; otherwise it adds the string to the pool. In more precise terms, the java.lang.String.intern method is used to obtain the canonical representation of the string; the result is a reference to the same class instance that would be returned if that string appeared as a literal. If an application interns a huge number of strings, the permanent generation might need to be increased from its default setting. </div>
<div>When this kind of error occurs, the text String.intern or ClassLoader.defineClass might appear near the top of the stack trace that is printed. </div>
<div>The jmap -permgen command prints statistics for the objects in the permanent generation, including information about internalized String instances. See 2.6.4 Getting Information on the Permanent Generation.</div>）<br />PermGen又是一个特殊内存区域：Classloader 加载的东东是不能回收的，它们放在PermGen中<br />（tomcat原文：Why does the memory usage increase when I redeploy a web application? Because the Classloader (and the Class objects it loaded) cannot be recycled. They are stored in the permanent heap generation by the JVM, and when you redepoy a new class loader is created, which loads another copy of all these classes. This can cause OufOfMemoryErrors eventually.）<br /><br />回到我的问题来，打开%java_home%bin\jvisualvm.exe&nbsp; (jdk6以上有)<br />查看tomcat的内存情况，点击tomcat标签下monitor , 当used heap&nbsp;= max heap&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; used PermGen = max PermGen 时tomcat还在启动中，一会就报错；<br />由于此前已经将myeclipse中server-&gt;tomcat-&gt;tomcatx.x-&gt;jdk的参数设置成-Xms64m -Xmx512m -XX:MaxPermSize=80m<br />但是monitor 画面中max heap为 256M<br />点击overview标签,发现jvm参数如下：<br />-Xms64m<br />-Xmx512m<br />-XX:MaxPermSize=80m<br />-Xms64m<br />-Xmx256m<br /><br />显然tomcat使用了最后的设置-Xms64m -Xmx256m；这个是在myeclipse的属性-》java-&gt;installed jres 中的vm启动参数里，双击默认启用的jre，把参数去掉或者设置成-Xms64m -Xmx512m<br />再回到server-&gt;tomcat-&gt;tomcatx.x-&gt;jdk中把-XX:MaxPermSize=80m修改为-XX:MaxPermSize=128m<br />保存后再启动tomcat，ok<br /><br />另外，使用jvisualvm持续监测tomcat内存情况发现heap区域的内存使用会在一个高峰后稳定降低，内存被回收了；<br />而PermGen区域的内存则是不断增加到达一个峰值后就不再增加，但之后此区的内存没有被回收，验证了上面的说法；<br /><br />多说两句：java的动态加载衍生出诸多框架，在空间上也暴露出问题，反射在时间上也存在效率问题：下面是组测试数据：<br />
<p>Java version 1.6.0_13<br />Java HotSpot(TM) Client VM<br />11.3-b02<br />Sun Microsystems Inc.</p>
<p>Direct access using member field:<br />&nbsp;47 125 47 46 46<br />&nbsp;average time = 66 ms.<br />Reference access to member field:<br />&nbsp;109 109 110 94 109<br />&nbsp;average time = 106 ms.<br />Reflection access to member field:<br />&nbsp;13094 12984 13063 13062 13094<br />&nbsp;average time = 13051 ms.</p>
<p>Java version 1.6.0_13<br />Java HotSpot(TM) Client VM<br />11.3-b02<br />Sun Microsystems Inc.</p>
<p>Direct call using member field:<br />&nbsp;47 31 109 109 31<br />&nbsp;average time = 70 ms.<br />Direct call using passed value:<br />&nbsp;16 16 16 31 15<br />&nbsp;average time = 20 ms.<br />Call to object using member field:<br />&nbsp;46 47 47 47 32<br />&nbsp;average time = 43 ms.<br />Call to object using passed value:<br />&nbsp;15 16 31 16 16<br />&nbsp;average time = 20 ms.<br />Reflection call using member field:<br />&nbsp;812 782 844 844 844<br />&nbsp;average time = 829 ms.<br />Reflection call using passed value:<br />&nbsp;938 953 954 1031 953<br />&nbsp;average time = 973 ms.</p>
<p>Java version 1.6.0_13<br />Java HotSpot(TM) Client VM<br />11.3-b02<br />Sun Microsystems Inc.</p>
<p>Direct Object creation:<br />&nbsp;62 47 78 32 93<br />&nbsp;average time = 63 ms.<br />Reflection Object creation:<br />&nbsp;125 94 94 109 187<br />&nbsp;average time = 121 ms.<br />Direct byte[8] creation:<br />&nbsp;125 187 94 172 94<br />&nbsp;average time = 137 ms.<br />Reflection byte[8] creation:<br />&nbsp;266 171 187 188 219<br />&nbsp;average time = 191 ms.<br />Direct byte[64] creation:<br />&nbsp;250 172 156 125 203<br />&nbsp;average time = 164 ms.<br />Reflection byte[64] creation:<br />&nbsp;281 219 203 203 219<br />&nbsp;average time = 211 ms.</p>华丽的上层需要坚实的底层基础<br /><br /><a href="http://wiki.apache.org/tomcat/FAQ/Deployment">http://wiki.apache.org/tomcat/FAQ/Deployment</a><br /><a href="http://download.oracle.com/javase/7/docs/webnotes/tsg/TSG-VM/html/memleaks.html#gbyuu">http://download.oracle.com/javase/7/docs/webnotes/tsg/TSG-VM/html/memleaks.html#gbyuu</a><br /><br /><img src ="http://www.blogjava.net/Todd/aggbug/361543.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2011-10-18 22:16 <a href="http://www.blogjava.net/Todd/archive/2011/10/18/361543.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java反射效率测试 jdk6</title><link>http://www.blogjava.net/Todd/archive/2011/09/22/359221.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Wed, 21 Sep 2011 23:18:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2011/09/22/359221.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/359221.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2011/09/22/359221.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/359221.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/359221.html</trackback:ping><description><![CDATA[<div><p>Java version 1.6.0_13<br /> Java HotSpot(TM) Client VM<br /> 11.3-b02<br /> Sun Microsystems Inc.</p> <p>Direct access using member field:<br /> &nbsp;47 125 47 46 46<br /> &nbsp;average time = 66 ms.<br /> Reference access to member field:<br /> &nbsp;109 109 110 94 109<br /> &nbsp;average time = 106 ms.<br /> Reflection access to member field:<br /> &nbsp;13094 12984 13063 13062 13094<br /> &nbsp;average time = 13051 ms.</p> <p>Java version 1.6.0_13<br /> Java HotSpot(TM) Client VM<br /> 11.3-b02<br /> Sun Microsystems Inc.</p> <p>Direct call using member field:<br /> &nbsp;47 31 109 109 31<br /> &nbsp;average time = 70 ms.<br /> Direct call using passed value:<br /> &nbsp;16 16 16 31 15<br /> &nbsp;average time = 20 ms.<br /> Call to object using member field:<br /> &nbsp;46 47 47 47 32<br /> &nbsp;average time = 43 ms.<br /> Call to object using passed value:<br /> &nbsp;15 16 31 16 16<br /> &nbsp;average time = 20 ms.<br /> Reflection call using member field:<br /> &nbsp;812 782 844 844 844<br /> &nbsp;average time = 829 ms.<br /> Reflection call using passed value:<br /> &nbsp;938 953 954 1031 953<br /> &nbsp;average time = 973 ms.</p> <p>Java version 1.6.0_13<br /> Java HotSpot(TM) Client VM<br /> 11.3-b02<br /> Sun Microsystems Inc.</p> <p>Direct Object creation:<br /> &nbsp;62 47 78 32 93<br /> &nbsp;average time = 63 ms.<br /> Reflection Object creation:<br /> &nbsp;125 94 94 109 187<br /> &nbsp;average time = 121 ms.<br /> Direct byte[8] creation:<br /> &nbsp;125 187 94 172 94<br /> &nbsp;average time = 137 ms.<br /> Reflection byte[8] creation:<br /> &nbsp;266 171 187 188 219<br /> &nbsp;average time = 191 ms.<br /> Direct byte[64] creation:<br /> &nbsp;250 172 156 125 203<br /> &nbsp;average time = 164 ms.<br /> Reflection byte[64] creation:<br /> &nbsp;281 219 203 203 219<br /> &nbsp;average time = 211 ms.</p> <p>&nbsp;</p></div><img src ="http://www.blogjava.net/Todd/aggbug/359221.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2011-09-22 07:18 <a href="http://www.blogjava.net/Todd/archive/2011/09/22/359221.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>eclipse wtp tomcat webservice</title><link>http://www.blogjava.net/Todd/archive/2011/05/06/349652.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Thu, 05 May 2011 17:45:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2011/05/06/349652.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/349652.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2011/05/06/349652.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/349652.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/349652.html</trackback:ping><description><![CDATA[<p>1.what's eclipse wtp </p>
<p>wtp(web tools platform )项目在eclipse平台上进行扩展，是一个开发j2ee web应用程序的工具集。wtp包含以下工具：</p>
<p>* 一个源码编辑器<a href="http://www.ssbbww.com/">www.ssbbww.com</a>用来编辑html, javascript, css, jsp, sql, xml, dtd, xsd, 和wsdl。</p>
<p>* 一个图形编辑器用来编辑xsd与wsdl。</p>
<p>* j2ee项目构建器和一个j2ee向导工具。</p>
<p>* 一个web服务创建向导和管理器，和ws-i 测试工具。</p>
<p>* 一个数据库访问，查询工具等。</p>
<p>wtp由两个子项目构成:wst(web标准工具集) 与jst(j2ee标准工具集)8ttt8 wtp是什么意思 是什么东西_什么叫-什么是wtp</p>
<p>wtp支付愿意原则，用于分析社会成员为项目所产出的效益愿意支付的价值。</p>
<p><br />
<br />
2.安装及工程创建发布：eclipse for java ee 里已经安装wtp,so不用咱们动手，eclipse 经典版安装wtp? <br />
工程的创建及发布<br />
见【传送门】：<a href="http://www.vogella.de/articles/EclipseWTP/article.html">http://www.vogella.de/articles/EclipseWTP/article.html</a><br />
<br />
3.wtp答疑见【传送门】<a href="http://wiki.eclipse.org/WTP_Tomcat_FAQ">http://wiki.eclipse.org/WTP_Tomcat_FAQ</a><br />
<br />
4.接着创建一个xfire webservice</p>
见 http://blog.csdn.net/xzmyzy/archive/2008/01/16/2047244.aspx <br />
http://blog.csdn.net/alex197963/archive/2009/07/20/4363328.aspx
<img src ="http://www.blogjava.net/Todd/aggbug/349652.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2011-05-06 01:45 <a href="http://www.blogjava.net/Todd/archive/2011/05/06/349652.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jadclipse 使用</title><link>http://www.blogjava.net/Todd/archive/2011/03/10/346074.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Thu, 10 Mar 2011 05:28:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2011/03/10/346074.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/346074.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2011/03/10/346074.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/346074.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/346074.html</trackback:ping><description><![CDATA[1.http://sourceforge.net/projects/jadclipse/      下载jadclipse,是个jar包，直接放到eclipse\plugins 目录下，重启eclipse;<br />2.<a href="http://www.varaneckas.com/jad" target="_blank">http://www.varaneckas.com/jad</a>      下载jad.exe,放到一个文件夹下，路径如d:\temp\jad.exe；<br />3.在eclipse-&gt;window-&gt;preferences-&gt;java-&gt;jadclipse      右侧 [path to decompiler] 输入 d:\temp\jad.exe<br />4.eclipse-&gt;window-&gt;General-&gt;Editors-&gt;file associations      右侧 [file types] -&gt;add   输入 *.class -&gt;ok<br />   选中新加的*.class,下面的[associated editors] 选中[jadclipse class file viewer] -&gt; default -&gt;ok <br />   重启 eclipse;打开一个class文件，即可查看反编译代码，欧了<img src ="http://www.blogjava.net/Todd/aggbug/346074.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2011-03-10 13:28 <a href="http://www.blogjava.net/Todd/archive/2011/03/10/346074.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汉字字符编码与转码详解</title><link>http://www.blogjava.net/Todd/archive/2010/05/23/321656.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Sun, 23 May 2010 05:19:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2010/05/23/321656.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/321656.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2010/05/23/321656.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/321656.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/321656.html</trackback:ping><description><![CDATA[程序中有汉字参数，经常会遇到编码转码问题，总结下：
<br />
<strong>1.汉字为多字节字符，须多字节编码解码</strong>，如"测试".getBytes("gb2312");
<br />
<br />
这样"测试".getBytes("gb2312")就变成一个byte数组，这时候你可以随意重新指定编码如iso-8859-1,
<br />
String s1=new String("测试".getBytes("gb2312"),"iso-8859-1");
<br />
<br />
编为s1,这是s1就变成一个是iso-8859-1编码的字符串，如果你想重新转为中文，那么，你用什么字符集编码的，必须用什么字符集来解
码，这里是iso-8859-1,可以这么来做
<br />
<br />
String s2 = new String(s1.getBytes("ISO-8859-1"),"gb2312");
<br />
<br />
<br />
这样s2又重新变回中文了，所以当你打印s2时，就是&#8220;测试&#8221;。
<br />
<br />
<br />
<strong>2.用iso-8859-1做中间编码，</strong>原因：
<br />
<br />
[1]iso-8859-1是单字节字符编码，
<br />
<br />
[2]ANSI 编码 (如：GB2312,
BIG5,Shift_JIS,ISO-8859-2等等），是多字节编码（英文单字节，中文多字节）；
<br />
<br />
[3]UNICODE 编码（UTF-8, UTF-7, UTF-16, UnicodeLittle,
UnicodeBig....）,是宽字节编码（所有字符均是多字节）
<br />
<br />
因此用iso-8859-1做中间码，会保持原有字节的秩序，不发生混乱；可以理解为其他的编码对iso-8859-1兼容吧。
<br />
<br />
因此，我们常常使用 bytes = string.getBytes("iso-8859-1")
坐中间码来进行逆向操作，得到原始的&#8220;字节串&#8221;。然后再使用正确的ANSI 编码，比如 string = new String(bytes,
"GB2312")，来得到正确的&#8220;UNICODE 字符串&#8221;。
<br />
<br />
不信的话可以试试，utf8和gb不能互相转换，只有iso-8859-1做中间码可以完美互相转码！！！
<img src ="http://www.blogjava.net/Todd/aggbug/321656.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2010-05-23 13:19 <a href="http://www.blogjava.net/Todd/archive/2010/05/23/321656.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jni javac++ 参数传递问题解决</title><link>http://www.blogjava.net/Todd/archive/2010/05/23/321655.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Sun, 23 May 2010 05:18:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2010/05/23/321655.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/321655.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2010/05/23/321655.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/321655.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/321655.html</trackback:ping><description><![CDATA[一，问题
<br />
1.多参数回传
<br />
2.参数传递出现乱码
<br />
<br />
二，解决
<br />
1.使用byte[]数组传入c++,在生成的头文件里就会变成jbyteArray 类型
<br />
例如，java 中参数：byte[]account,头文件里参数变成jbyteArray account，
<br />
通过c++修改完account的值后，java要获取该值，直接使用
<br />
jbyte* jbAccount = (env)-&gt;GetByteArrayElements(env, account, 0);
<br />
char* szAccount = (char*)jbAccount;
<br />
指针的地址并不是account的地址，最后赋下值才行
<br />
env-&gt;SetByteArrayRegion(account,0,strlen(szAccount),jbyte*
jbAccount);
<br />
<br />
2.String.getBytes()生成的byte数组传入c++后，在字符串的结尾会有多余乱码，
<br />
解决办法是，传入byte数组时，把数组的长度length，也传入c++,
<br />
令接受的数组strBuff[length]='\0';即可解决问题
<img src ="http://www.blogjava.net/Todd/aggbug/321655.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2010-05-23 13:18 <a href="http://www.blogjava.net/Todd/archive/2010/05/23/321655.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java dom4j操作xml</title><link>http://www.blogjava.net/Todd/archive/2010/05/22/321618.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Sat, 22 May 2010 10:26:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2010/05/22/321618.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/321618.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2010/05/22/321618.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/321618.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/321618.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.blogjava.net/Todd/archive/2010/05/22/321618.html'>阅读全文</a><img src ="http://www.blogjava.net/Todd/aggbug/321618.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2010-05-22 18:26 <a href="http://www.blogjava.net/Todd/archive/2010/05/22/321618.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汉字字符编码与转码详解</title><link>http://www.blogjava.net/Todd/archive/2010/04/26/319387.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Mon, 26 Apr 2010 06:03:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2010/04/26/319387.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/319387.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2010/04/26/319387.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/319387.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/319387.html</trackback:ping><description><![CDATA[程序中有汉字参数，经常会遇到编码转码问题，总结下：<br />
1.汉字为多字节字符，须多字节编码解码，如"测试".getBytes("gb2312");<br />
<pre>这样"测试".getBytes("gb2312")就变成一个byte数组，这时候你可以随意重新指定编码如iso-8859-1,<br />
<br />
String s1=new String("测试".getBytes("gb2312"),"iso-8859-1");<br />
<br />
编为s1,这是s1就变成一个是iso-8859-1编码的字符串，如果你想重新转为中文，那么，你用什么字符集编码的，必须用什么字符集来解码，这里是iso-8859-1,<br />
<br />
可以这么来做<br />
<br />
<br />
<br />
String s2 = new String(s1.getBytes("ISO-8859-1"),"gb2312");<br />
<br />
这样s2又重新变回中文了，所以当你打印s2时，就是&#8220;测试&#8221;。<br />
<br />
<br />
<br />
2.用iso-8859-1做中间编码，原因：<br />
[1]iso-8859-1是单字节字符编码，<br />
[2]ANSI 编码 (如：GB2312, BIG5,Shift_JIS,ISO-8859-2等等），是多字节编码（英文单字节，中文多字节）；<br />
[3]UNICODE 编码（UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig....）,是宽字节编码（所有字符均是多字节）<br />
因此用iso-8859-1做中间码，会保持原有字节的秩序，不发生混乱；可以理解为其他的编码对iso-8859-1兼容吧。<br />
<br />
因此，我们常常使用 bytes = string.getBytes("iso-8859-1") 坐中间码来进行逆向操作，得到原始的&#8220;字节串&#8221;。然后再使用正确的<br />
ANSI 编码，比如 string = new String(bytes, "GB2312")，来得到正确的&#8220;UNICODE 字符串&#8221;。<br />
<br />
不信的话可以试试，utf8和gb不能互相转换，只有iso-8859-1做中间码可以完美互相转码！！！<br />
</pre>
<br />
<br />
<img src ="http://www.blogjava.net/Todd/aggbug/319387.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2010-04-26 14:03 <a href="http://www.blogjava.net/Todd/archive/2010/04/26/319387.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>四个有用的过虑器</title><link>http://www.blogjava.net/Todd/archive/2010/04/24/319270.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Sat, 24 Apr 2010 08:36:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2010/04/24/319270.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/319270.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2010/04/24/319270.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/319270.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/319270.html</trackback:ping><description><![CDATA[一、使浏览器不缓存页面的过滤器
<p>import javax.servlet.*;<br />
import javax.servlet.http.HttpServletResponse;<br />
import java.io.IOException;</p>
<p>/**<br />
&nbsp;* 用于的使 Browser 不缓存页面的过滤器<br />
&nbsp;*/<br />
public class ForceNoCacheFilter&nbsp;implements Filter {</p>
<p>&nbsp;public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException<br />
&nbsp;{<br />
&nbsp;&nbsp;((HttpServletResponse) response).setHeader("Cache-Control","no-cache");<br />
&nbsp;&nbsp;((HttpServletResponse) response).setHeader("Pragma","no-cache");<br />
&nbsp;&nbsp;((HttpServletResponse) response).setDateHeader ("Expires", -1);<br />
&nbsp;&nbsp;filterChain.doFilter(request, response);<br />
&nbsp;}</p>
<p>&nbsp;public void destroy()<br />
&nbsp;{<br />
&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp; public void init(FilterConfig filterConfig) throws ServletException<br />
&nbsp;{<br />
&nbsp;}<br />
}</p>
<p>二、检测用户是否登陆的过滤器</p>
<p>import javax.servlet.*;<br />
import javax.servlet.http.HttpServletRequest;<br />
import javax.servlet.http.HttpServletResponse;<br />
import javax.servlet.http.HttpSession;<br />
import java.util.List;<br />
import java.util.ArrayList;<br />
import java.util.StringTokenizer;<br />
import java.io.IOException;</p>
<p>/**<br />
&nbsp;* 用于检测用户是否登陆的过滤器，如果未登录，则重定向到指的登录页面&lt;p&gt;<br />
&nbsp;* 配置参数&lt;p&gt;<br />
&nbsp;* checkSessionKey 需检查的在 Session 中保存的关键字&lt;br/&gt;<br />
&nbsp;* redirectURL 如果用户未登录，则重定向到指定的页面，URL不包括 ContextPath&lt;br/&gt;<br />
&nbsp;* notCheckURLList 不做检查的URL列表，以分号分开，并且 URL 中不包括 ContextPath&lt;br/&gt;<br />
&nbsp;*/<br />
public class CheckLoginFilter<br />
&nbsp;implements Filter<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;protected FilterConfig filterConfig = null;<br />
&nbsp;&nbsp;&nbsp; private String redirectURL = null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;private List notCheckURLList = new ArrayList();<br />
&nbsp;&nbsp;&nbsp;&nbsp;private String sessionKey = null;</p>
<p>&nbsp;public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException<br />
&nbsp;{<br />
&nbsp;&nbsp;HttpServletRequest request = (HttpServletRequest) servletRequest;<br />
&nbsp;&nbsp;HttpServletResponse response = (HttpServletResponse) servletResponse;</p>
<p>&nbsp;&nbsp; HttpSession session = request.getSession();<br />
&nbsp;&nbsp;if(sessionKey == null)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;filterChain.doFilter(request, response);<br />
&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;if((!checkRequestURIIntNotFilterList(request)) &amp;&amp; session.getAttribute(sessionKey) == null)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;response.sendRedirect(request.getContextPath() + redirectURL);<br />
&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;filterChain.doFilter(servletRequest, servletResponse);<br />
&nbsp;}</p>
<p>&nbsp;public void destroy()<br />
&nbsp;{<br />
&nbsp;&nbsp;notCheckURLList.clear();<br />
&nbsp;}</p>
<p>&nbsp;private boolean checkRequestURIIntNotFilterList(HttpServletRequest request)<br />
&nbsp;{<br />
&nbsp;&nbsp;String uri = request.getServletPath() + (request.getPathInfo() == null ? "" : request.getPathInfo());<br />
&nbsp;&nbsp;return notCheckURLList.contains(uri);<br />
&nbsp;}</p>
<p>&nbsp;public void init(FilterConfig filterConfig) throws ServletException<br />
&nbsp;{<br />
&nbsp;&nbsp;this.filterConfig = filterConfig;<br />
&nbsp;&nbsp;redirectURL = filterConfig.getInitParameter("redirectURL");<br />
&nbsp; sessionKey = filterConfig.getInitParameter("checkSessionKey");</p>
<p>&nbsp;&nbsp;String notCheckURLListStr = filterConfig.getInitParameter("notCheckURLList");</p>
<p>&nbsp;&nbsp;if(notCheckURLListStr != null)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;StringTokenizer st = new StringTokenizer(notCheckURLListStr, ";");<br />
&nbsp;&nbsp;&nbsp;notCheckURLList.clear();<br />
&nbsp;&nbsp;&nbsp;while(st.hasMoreTokens())<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;notCheckURLList.add(st.nextToken());<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;}<br />
}</p>
<p>三、字符编码的过滤器</p>
<p>import javax.servlet.*;<br />
import java.io.IOException;</p>
<p>/**<br />
&nbsp;* 用于设置 HTTP 请求字符编码的过滤器，通过过滤器参数encoding指明使用何种字符编码,用于处理Html Form请求参数的中文问题<br />
&nbsp;*/<br />
public class CharacterEncodingFilter<br />
&nbsp;implements Filter<br />
{<br />
&nbsp;protected FilterConfig filterConfig = null;<br />
&nbsp;protected String encoding = "";</p>
<p>&nbsp;public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException<br />
&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(encoding != null)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; servletRequest.setCharacterEncoding(encoding);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;filterChain.doFilter(servletRequest, servletResponse);<br />
&nbsp;}</p>
<p>&nbsp;public void destroy()<br />
&nbsp;{<br />
&nbsp;&nbsp;filterConfig = null;<br />
&nbsp;&nbsp;encoding = null;<br />
&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp; public void init(FilterConfig filterConfig) throws ServletException<br />
&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.filterConfig = filterConfig;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.encoding = filterConfig.getInitParameter("encoding");</p>
<p>&nbsp;}<br />
}</p>
<p>四、资源保护过滤器</p>
<p>
<pre>package catalog.view.util;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import java.util.HashSet;
//
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This Filter class handle the security of the application.
*
<p>
* It should be configured inside the web.xml.
*
* @author <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#100;&#101;&#114;&#101;&#107;&#95;&#115;&#104;&#101;&#110;&#64;&#104;&#111;&#116;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;">Derek Y. Shen</a>
*/
public class SecurityFilter implements Filter {
//the login page uri
private static final String LOGIN_PAGE_URI = "login.jsf";
//the logger object
private Log logger = LogFactory.getLog(this.getClass());
//a set of restricted resources
private Set restrictedResources;
/**
* Initializes the Filter.
*/
public void init(FilterConfig filterConfig) throws ServletException {
this.restrictedResources = new HashSet();
this.restrictedResources.add("/createProduct.jsf");
this.restrictedResources.add("/editProduct.jsf");
this.restrictedResources.add("/productList.jsf");
}
/**
* Standard doFilter object.
*/
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
this.logger.debug("doFilter");
String contextPath = ((HttpServletRequest)req).getContextPath();
String requestUri = ((HttpServletRequest)req).getRequestURI();
this.logger.debug("contextPath = " + contextPath);
this.logger.debug("requestUri = " + requestUri);
if (this.contains(requestUri, contextPath) &amp;&amp; !this.authorize((HttpServletRequest)req)) {
this.logger.debug("authorization failed");
((HttpServletRequest)req).getRequestDispatcher(LOGIN_PAGE_URI).forward(req, res);
}
else {
this.logger.debug("authorization succeeded");
chain.doFilter(req, res);
}
}
public void destroy() {}
private boolean contains(String value, String contextPath) {
Iterator ite = this.restrictedResources.iterator();
while (ite.hasNext()) {
String restrictedResource = (String)ite.next();
if ((contextPath + restrictedResource).equalsIgnoreCase(value)) {
return true;
}
}
return false;
}
private boolean authorize(HttpServletRequest req) {
//处理用户登录
/*	UserBean user = (UserBean)req.getSession().getAttribute(BeanNames.USER_BEAN);
if (user != null &amp;&amp; user.getLoggedIn()) {
//user logged in
return true;
}
else {
return false;
}*/
}
}</p>
</pre>
<img src ="http://www.blogjava.net/Todd/aggbug/319270.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2010-04-24 16:36 <a href="http://www.blogjava.net/Todd/archive/2010/04/24/319270.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】单例模式不可使用双重检查锁定</title><link>http://www.blogjava.net/Todd/archive/2010/04/21/318917.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Tue, 20 Apr 2010 16:49:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2010/04/21/318917.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/318917.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2010/04/21/318917.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/318917.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/318917.html</trackback:ping><description><![CDATA[<br />
转自：http://www.ibm.com/developerworks/cn/java/j-dcl.html<br />
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="32" sizcache="2">
    <tbody sizset="32" sizcache="1">
        <tr valign="top">
            <td width="100%">
            <h1>&nbsp;</h1>
            <h1>双重检查锁定及单例模式</h1>
            <p id="subtitle"><em>全面理解这一失效的编程习语</em></p>
            <img class="display-img" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="1" /></td>
            <td class="no-print" width="192"><img height="18" alt="developerWorks" src="http://www.ibm.com/developerworks/i/dw.gif" width="192" /></td>
        </tr>
    </tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="33" sizcache="2">
    <tbody sizset="34" sizcache="2">
        <tr valign="top" sizset="34" sizcache="2">
            <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
            <td width="100%" sizset="34" sizcache="2">
            <table class="no-print" cellspacing="0" cellpadding="0" width="160" align="right" border="0" sizset="34" sizcache="2">
                <tbody sizset="35" sizcache="2">
                    <tr sizset="35" sizcache="2">
                        <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
                        <td sizset="35" sizcache="2">
                        <table cellspacing="0" cellpadding="0" width="150" border="0" sizset="35" sizcache="2">
                            <tbody sizset="35" sizcache="1">
                                <tr>
                                    <td class="v14-header-1-small">文档选项</td>
                                </tr>
                            </tbody>
                        </table>
                        <table class="v14-gray-table-border" cellspacing="0" cellpadding="0" border="0" sizset="36" sizcache="2">
                            <tbody sizset="37" sizcache="2">
                                <tr sizset="37" sizcache="2">
                                    <td class="no-padding" width="150" sizset="37" sizcache="2">
                                    <table cellspacing="0" cellpadding="0" width="143" border="0" sizset="37" sizcache="2">
                                        <script language="JavaScript" type="text/javascript">
<!-- document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt="" /></td><td width="16"><img alt="将打印机的版面设置成横向打印模式" height="16" src="//www.ibm.com/i/v14/icons/printer.gif" width="16" vspace="3"  /></td><td width="122"><p><strong><a class="smallplainlink" href="javascript:print()">打印本页</a></strong></p></td></tr>');
//-->
</script>
                                        <tbody sizset="37" sizcache="1">
                                            <tr valign="top">
                                                <td width="8"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" /></td>
                                                <td width="16"><img height="16" alt="将打印机的版面设置成横向打印模式" src="http://www.ibm.com/i/v14/icons/printer.gif" width="16" vspace="3" /></td>
                                                <td width="122">
                                                <p><strong><a class="smallplainlink" href="javascript:print()">打印本页</a></strong></p>
                                                </td>
                                            </tr>
                                            <noscript></noscript>
                                            <form name="email" action="https://www.ibm.com/developerworks/secure/email-it.jsp" sizset="38" sizcache="1">
                                                <input type="hidden" value="所有的编程语言都有一些共用的习语。了解和使用一些习语很有用，程序员们花费宝贵的时间来创建、学习和实现这些习语。问题是，稍后经过证明，一些习语并不完全如其所声称的那样，或者仅仅是与描述的功能不符。Java 编程语言也不例外，其中的双重检查锁定就是这样的一个绝不应该使用的习语。在本文中，Peter Haggar 介绍了双重检查锁定习语的渊源，开发它的原因和它失效的原因。" name="body" /><input type="hidden" value="双重检查锁定及单例模式" name="subject" /><input type="hidden" value="cn" name="lang" /> <script language="JavaScript" type="text/javascript">
<!-- document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt="" /></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送"  /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><strong>将此页作为电子邮件发送</strong></a></p></td></tr>');
//-->
</script>
                                                <tr valign="top">
                                                    <td width="8"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" /></td>
                                                    <td width="16"><img height="16" alt="将此页作为电子邮件发送" src="http://www.ibm.com/i/v14/icons/em.gif" width="16" vspace="3" /></td>
                                                    <td width="122">
                                                    <p><a class="smallplainlink" href="javascript:document.email.submit();"><strong>将此页作为电子邮件发送</strong></a></p>
                                                    </td>
                                                </tr>
                                                <noscript>
                                                <tr valign="top">
                                                    <td width="8"><img alt="" height="1" width="8" src="//www.ibm.com/i/c.gif"   /></td>
                                                    <td width="16"><img alt="" width="16" height="16" src="//www.ibm.com/i/c.gif"   /></td>
                                                    <td class="small" width="122">
                                                    <p><span class="ast">未显示需要 JavaScript 的文档选项</span></p>
                                                    </td>
                                                </tr>
                                                </noscript>
                                            </form>
                                            <tr valign="top">
                                                <td width="8"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" /></td>
                                                <td width="16"><img height="16" alt="英文原文 " src="http://www.ibm.com/i/v14/icons/fw_bold.gif" width="16" vspace="3" /></td>
                                                <td width="122">
                                                <p><a class="smallplainlink" onmouseover="linkQueryAppend(this)" href="http://www.ibm.com/developerworks/java/library/j-dcl.html"><strong>英文原文</strong></a></p>
                                                </td>
                                            </tr>
                                        </tbody>
                                    </table>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        <!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- this content will be automatically generated across all content areas --><!--end RESERVED FOR FUTURE USE INCLUDE FILES--><br />
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>级别： 中级</p>
            <p><a href="http://www.ibm.com/developerworks/cn/java/j-dcl.html#author">Peter Haggar</a>, 高级软件工程师, IBM<br />
            </p>
            <p>2004 年 5 月 01 日</p>
            <blockquote>所有的编程语言都有一些共用的习语。了解和使用一些习语很有用，程序员们花费宝贵的时间来创建、学习和实现这些习语。问题是，稍后经过证明，一些习语并不完全如其所声称的那样，或者仅仅是与描述的功能不符。在 Java 编程语言中，双重检查锁定就是这样的一个绝不应该使用的习语。在本文中，Peter Haggar 介绍了双重检查锁定习语的渊源，开发它的原因和它失效的原因。 </blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
            <p><em><strong>编辑注</strong>：本文在针对 Java 5.0 修订前参考了 Java 内存模型；关于内存排序的描述也许不再正确。尽管如此，在新的内存模型中，双重检查锁定习语仍旧是无效的。</em> </p>
            <p>单例创建模式是一个通用的编程习语。和多线程一起使用时，必需使用某种类型的同步。在努力创建更有效的代码时，Java 程序员们创建了双重检查锁定习语，将其和单例创建模式一起使用，从而限制同步代码量。然而，由于一些不太常见的 Java 内存模型细节的原因，并不能保证这个双重检查锁定习语有效。它偶尔会失败，而不是总失败。此外，它失败的原因并不明显，还包含 Java 内存模型的一些隐秘细节。这些事实将导致代码失败，原因是双重检查锁定难于跟踪。在本文余下的部分里，我们将详细介绍双重检查锁定习语，从而理解它在何处失效。</p>
            <p><a name="1"><span class="atitle">单例创建习语</span></a></p>
            <p>要理解双重检查锁定习语是从哪里起源的，就必须理解通用单例创建习语，如清单 1 中的阐释：</p>
            <br />
            <a name="code1"><strong>清单 1. 单例创建习语</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="65%" border="0" sizset="40" sizcache="2">
                <tbody sizset="40" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">                        import java.util.*;
                        class Singleton
                        {
                        private static Singleton instance;
                        private Vector v;
                        private boolean inUse;
                        private Singleton()
                        {
                        v = new Vector();
                        v.addElement(new Object());
                        inUse = true;
                        }
                        public static Singleton getInstance()
                        {
                        if (instance == null)          //1
                        instance = new Singleton();  //2
                        return instance;               //3
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>此类的设计确保只创建一个 <code>Singleton</code> 对象。构造函数被声明为 <code>private</code>，<code>getInstance()</code> 方法只创建一个对象。这个实现适合于单线程程序。然而，当引入多线程时，就必须通过同步来保护 <code>getInstance()</code> 方法。如果不保护 <code>getInstance()</code> 方法，则可能返回 <code>Singleton</code> 对象的两个不同的实例。假设两个线程并发调用 <code>getInstance()</code> 方法并且按以下顺序执行调用： </p>
            <ol>
                <li>线程 1 调用 <code>getInstance()</code> 方法并决定 <code>instance</code> 在 //1 处为 <code>null</code>。 <br />
                <br />
                <li>线程 1 进入 <code>if</code> 代码块，但在执行 //2 处的代码行时被线程 2 预占。 <br />
                <br />
                <li>线程 2 调用 <code>getInstance()</code> 方法并在 //1 处决定 <code>instance</code> 为 <code>null</code>。 <br />
                <br />
                <li>线程 2 进入 <code>if</code> 代码块并创建一个新的 <code>Singleton</code> 对象并在 //2 处将变量 <code>instance</code> 分配给这个新对象。 <br />
                <br />
                <li>线程 2 在 //3 处返回 <code>Singleton</code> 对象引用。<br />
                <br />
                <li>线程 2 被线程 1 预占。 <br />
                <br />
                <li>线程 1 在它停止的地方启动，并执行 //2 代码行，这导致创建另一个 <code>Singleton</code> 对象。 <br />
                <br />
                <li>线程 1 在 //3 处返回这个对象。 </li>
            </ol>
            <p>结果是 <code>getInstance()</code> 方法创建了两个 <code>Singleton</code> 对象，而它本该只创建一个对象。通过同步 <code>getInstance()</code> 方法从而在同一时间只允许一个线程执行代码，这个问题得以改正，如清单 2 所示： </p>
            <br />
            <a name="code2"><strong>清单 2. 线程安全的 getInstance() 方法</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="41" sizcache="2">
                <tbody sizset="41" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">                        public static synchronized Singleton getInstance()
                        {
                        if (instance == null)          //1
                        instance = new Singleton();  //2
                        return instance;               //3
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>清单 2 中的代码针对多线程访问 <code>getInstance()</code> 方法运行得很好。然而，当分析这段代码时，您会意识到只有在第一次调用方法时才需要同步。由于只有第一次调用执行了 //2 处的代码，而只有此行代码需要同步，因此就无需对后续调用使用同步。所有其他调用用于决定 <code>instance</code> 是非 <code>null</code> 的，并将其返回。多线程能够安全并发地执行除第一次调用外的所有调用。尽管如此，由于该方法是 <code>synchronized</code> 的，需要为该方法的每一次调用付出同步的代价，即使只有第一次调用需要同步。 </p>
            <p>为使此方法更为有效，一个被称为双重检查锁定的习语就应运而生了。这个想法是为了避免对除第一次调用外的所有调用都实行同步的昂贵代价。同步的代价在不同的 JVM 间是不同的。在早期，代价相当高。随着更高级的 JVM 的出现，同步的代价降低了，但出入 <code>synchronized</code> 方法或块仍然有性能损失。不考虑 JVM 技术的进步，程序员们绝不想不必要地浪费处理时间。</p>
            <p>因为只有清单 2 中的 //2 行需要同步，我们可以只将其包装到一个同步块中，如清单 3 所示： </p>
            <br />
            <a name="code3"><strong>清单 3. getInstance() 方法</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="42" sizcache="2">
                <tbody sizset="42" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">                        public static Singleton getInstance()
                        {
                        if (instance == null)
                        {
                        synchronized(Singleton.class) {
                        instance = new Singleton();
                        }
                        }
                        return instance;
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>清单 3 中的代码展示了用多线程加以说明的和清单 1 相同的问题。当 <code>instance</code> 为 <code>null</code> 时，两个线程可以并发地进入 <code>if</code> 语句内部。然后，一个线程进入 <code>synchronized</code> 块来初始化 <code>instance</code>，而另一个线程则被阻断。当第一个线程退出 <code>synchronized</code> 块时，等待着的线程进入并创建另一个 <code>Singleton</code> 对象。注意：当第二个线程进入 <code>synchronized</code> 块时，它并没有检查 <code>instance</code> 是否非 <code>null</code>。 </p>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="43" sizcache="2">
                <tbody sizset="43" sizcache="1">
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="44" sizcache="2">
                <tbody sizset="45" sizcache="2">
                    <tr align="right" sizset="45" sizcache="2">
                        <td sizset="45" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0" sizset="45" sizcache="2">
                            <tbody sizset="45" sizcache="1">
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-dcl.html#main"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="2"><span class="atitle">双重检查锁定</span></a></p>
            <p>为处理清单 3 中的问题，我们需要对 <code>instance</code> 进行第二次检查。这就是&#8220;双重检查锁定&#8221;名称的由来。将双重检查锁定习语应用到清单 3 的结果就是清单 4 。 </p>
            <br />
            <a name="code4"><strong>清单 4. 双重检查锁定示例</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="46" sizcache="2">
                <tbody sizset="46" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">                        public static Singleton getInstance()
                        {
                        if (instance == null)
                        {
                        synchronized(Singleton.class) {  //1
                        if (instance == null)          //2
                        instance = new Singleton();  //3
                        }
                        }
                        return instance;
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>双重检查锁定背后的理论是：在 //2 处的第二次检查使（如清单 3 中那样）创建两个不同的 <code>Singleton</code> 对象成为不可能。假设有下列事件序列： </p>
            <ol>
                <li>线程 1 进入 <code>getInstance()</code> 方法。 <br />
                <br />
                <li>由于 <code>instance</code> 为 <code>null</code>，线程 1 在 //1 处进入 <code>synchronized</code> 块。 <br />
                <br />
                <li>线程 1 被线程 2 预占。<br />
                <br />
                <li>线程 2 进入 <code>getInstance()</code> 方法。<br />
                <br />
                <li>由于 <code>instance</code> 仍旧为 <code>null</code>，线程 2 试图获取 //1 处的锁。然而，由于线程 1 持有该锁，线程 2 在 //1 处阻塞。<br />
                <br />
                <li>线程 2 被线程 1 预占。<br />
                <br />
                <li>线程 1 执行，由于在 //2 处实例仍旧为 <code>null</code>，线程 1 还创建一个 <code>Singleton</code> 对象并将其引用赋值给 <code>instance</code>。<br />
                <br />
                <li>线程 1 退出 <code>synchronized</code> 块并从 <code>getInstance()</code> 方法返回实例。 <br />
                <br />
                <li>线程 1 被线程 2 预占。<br />
                <br />
                <li>线程 2 获取 //1 处的锁并检查 <code>instance</code> 是否为 <code>null</code>。 <br />
                <br />
                <li>由于 <code>instance</code> 是非 <code>null</code> 的，并没有创建第二个 <code>Singleton</code> 对象，由线程 1 创建的对象被返回。 </li>
            </ol>
            <p>双重检查锁定背后的理论是完美的。不幸地是，现实完全不同。双重检查锁定的问题是：并不能保证它会在单处理器或多处理器计算机上顺利运行。</p>
            <p>双重检查锁定失败的问题并不归咎于 JVM 中的实现 bug，而是归咎于 Java 平台内存模型。内存模型允许所谓的&#8220;无序写入&#8221;，这也是这些习语失败的一个主要原因。</p>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="47" sizcache="2">
                <tbody sizset="47" sizcache="1">
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="48" sizcache="2">
                <tbody sizset="49" sizcache="2">
                    <tr align="right" sizset="49" sizcache="2">
                        <td sizset="49" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0" sizset="49" sizcache="2">
                            <tbody sizset="49" sizcache="1">
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-dcl.html#main"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="3"><span class="atitle">无序写入</span></a></p>
            <p>为解释该问题，需要重新考察上述清单 4 中的 //3 行。此行代码创建了一个 <code>Singleton</code> 对象并初始化变量 <code>instance</code> 来引用此对象。这行代码的问题是：在 <code>Singleton</code> 构造函数体执行之前，变量 <code>instance</code> 可能成为非 <code>null</code> 的。</p>
            <p>什么？这一说法可能让您始料未及，但事实确实如此。在解释这个现象如何发生前，请先暂时接受这一事实，我们先来考察一下双重检查锁定是如何被破坏的。假设清单 4 中代码执行以下事件序列：</p>
            <ol>
                <li>线程 1 进入 <code>getInstance()</code> 方法。<br />
                <br />
                <li>由于 <code>instance</code> 为 <code>null</code>，线程 1 在 //1 处进入 <code>synchronized</code> 块。 <br />
                <br />
                <li>线程 1 前进到 //3 处，但在构造函数执行<em>之前</em>，使实例成为非 <code>null</code>。 <br />
                <br />
                <li>线程 1 被线程 2 预占。<br />
                <br />
                <li>线程 2 检查实例是否为 <code>null</code>。因为实例不为 null，线程 2 将 <code>instance</code> 引用返回给一个构造完整但部分初始化了的 <code>Singleton</code> 对象。 <br />
                <br />
                <li>线程 2 被线程 1 预占。<br />
                <br />
                <li>线程 1 通过运行 <code>Singleton</code> 对象的构造函数并将引用返回给它，来完成对该对象的初始化。 </li>
            </ol>
            <p>此事件序列发生在线程 2 返回一个尚未执行构造函数的对象的时候。</p>
            <p>为展示此事件的发生情况，假设为代码行 <code>instance =new Singleton();</code> 执行了下列伪代码： <code>instance =new Singleton();</code> </p>
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="50" sizcache="2">
                <tbody sizset="50" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">mem = allocate();             //Allocate memory for Singleton object.
                        instance = mem;               //Note that instance is now non-null, but
                        //has not been initialized.
                        ctorSingleton(instance);      //Invoke constructor for Singleton passing
                        //instance.
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>这段伪代码不仅是可能的，而且是一些 JIT 编译器上真实发生的。执行的顺序是颠倒的，但鉴于当前的内存模型，这也是允许发生的。JIT 编译器的这一行为使双重检查锁定的问题只不过是一次学术实践而已。</p>
            <p>为说明这一情况，假设有清单 5 中的代码。它包含一个剥离版的 <code>getInstance()</code> 方法。我已经删除了&#8220;双重检查性&#8221;以简化我们对生成的汇编代码（清单 6）的回顾。我们只关心 JIT 编译器如何编译 <code>instance=new Singleton();</code> 代码。此外，我提供了一个简单的构造函数来明确说明汇编代码中该构造函数的运行情况。 </p>
            <br />
            <a name="code5"><strong>清单 5. 用于演示无序写入的单例类</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="51" sizcache="2">
                <tbody sizset="51" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">                        class Singleton
                        {
                        private static Singleton instance;
                        private boolean inUse;
                        private int val;
                        private Singleton()
                        {
                        inUse = true;
                        val = 5;
                        }
                        public static Singleton getInstance()
                        {
                        if (instance == null)
                        instance = new Singleton();
                        return instance;
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>清单 6 包含由 Sun JDK 1.2.1 JIT 编译器为清单 5 中的 <code>getInstance()</code> 方法体生成的汇编代码。 </p>
            <br />
            <a name="code6"><strong>清单 6. 由清单 5 中的代码生成的汇编代码</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="52" sizcache="2">
                <tbody sizset="52" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">                        ;asm code generated for getInstance
                        054D20B0   mov         eax,[049388C8]      ;load instance ref
                        054D20B5   test        eax,eax             ;test for null
                        054D20B7   jne         054D20D7
                        054D20B9   mov         eax,14C0988h
                        054D20BE   call        503EF8F0            ;allocate memory
                        054D20C3   mov         [049388C8],eax      ;store pointer in
                        ;instance ref. instance
                        ;non-null and ctor
                        ;has not run
                        054D20C8   mov         ecx,dword ptr [eax]
                        054D20CA   mov         dword ptr [ecx],1   ;inline ctor - inUse=true;
                        054D20D0   mov         dword ptr [ecx+4],5 ;inline ctor - val=5;
                        054D20D7   mov         ebx,dword ptr ds:[49388C8h]
                        054D20DD   jmp         054D20B0
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p><strong>注:</strong> 为引用下列说明中的汇编代码行，我将引用指令地址的最后两个值，因为它们都以 <code>054D20</code> 开头。例如，<code>B5</code> 代表 <code>test eax,eax</code>。</p>
            <p>汇编代码是通过运行一个在无限循环中调用 <code>getInstance()</code> 方法的测试程序来生成的。程序运行时，请运行 Microsoft Visual C++ 调试器并将其附到表示测试程序的 Java 进程中。然后，中断执行并找到表示该无限循环的汇编代码。</p>
            <p><code>B0</code> 和 <code>B5</code> 处的前两行汇编代码将 <code>instance</code> 引用从内存位置 <code>049388C8</code> 加载至 <code>eax</code> 中，并进行 <code>null</code> 检查。这跟清单 5 中的 <code>getInstance()</code> 方法的第一行代码相对应。第一次调用此方法时，<code>instance</code> 为 <code>null</code>，代码执行到 <code>B9</code>。<code>BE</code> 处的代码为 <code>Singleton</code> 对象从堆中分配内存，并将一个指向该块内存的指针存储到 <code>eax</code> 中。下一行代码，<code>C3</code>，获取 <code>eax</code> 中的指针并将其存储回内存位置为 <code>049388C8</code> 的实例引用。结果是，<code>instance</code> 现在为非 <code>null</code> 并引用一个有效的 <code>Singleton</code> 对象。然而，此对象的构造函数尚未运行，这恰是破坏双重检查锁定的情况。然后，在 <code>C8</code> 行处，<code>instance</code> 指针被解除引用并存储到 <code>ecx</code>。<code>CA</code> 和 <code>D0</code> 行表示内联的构造函数，该构造函数将值 <code>true</code> 和 <code>5</code> 存储到 <code>Singleton</code> 对象。如果此代码在执行 <code>C3</code> 行后且在完成该构造函数前被另一个线程中断，则双重检查锁定就会失败。</p>
            <p>不是所有的 JIT 编译器都生成如上代码。一些生成了代码，从而只在构造函数执行后使 <code>instance</code> 成为非 <code>null</code>。针对 Java 技术的 IBM SDK 1.3 版和 Sun JDK 1.3 都生成这样的代码。然而，这并不意味着应该在这些实例中使用双重检查锁定。该习语失败还有一些其他原因。此外，您并不总能知道代码会在哪些 JVM 上运行，而 JIT 编译器总是会发生变化，从而生成破坏此习语的代码。</p>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="53" sizcache="2">
                <tbody sizset="53" sizcache="1">
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="54" sizcache="2">
                <tbody sizset="55" sizcache="2">
                    <tr align="right" sizset="55" sizcache="2">
                        <td sizset="55" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0" sizset="55" sizcache="2">
                            <tbody sizset="55" sizcache="1">
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-dcl.html#main"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="4"><span class="atitle">双重检查锁定：获取两个</span></a></p>
            <p>考虑到当前的双重检查锁定不起作用，我加入了另一个版本的代码，如清单 7 所示，从而防止您刚才看到的无序写入问题。 </p>
            <br />
            <a name="code7"><strong>清单 7. 解决无序写入问题的尝试</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="56" sizcache="2">
                <tbody sizset="56" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">                        public static Singleton getInstance()
                        {
                        if (instance == null)
                        {
                        synchronized(Singleton.class) {      //1
                        Singleton inst = instance;         //2
                        if (inst == null)
                        {
                        synchronized(Singleton.class) {  //3
                        inst = new Singleton();        //4
                        }
                        instance = inst;                 //5
                        }
                        }
                        }
                        return instance;
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>看着清单 7 中的代码，您应该意识到事情变得有点荒谬。请记住，创建双重检查锁定是为了避免对简单的三行 <code>getInstance()</code> 方法实现同步。清单 7 中的代码变得难于控制。另外，该代码没有解决问题。仔细检查可获悉原因。</p>
            <p>此代码试图避免无序写入问题。它试图通过引入局部变量 <code>inst</code> 和第二个 <code>synchronized</code> 块来解决这一问题。该理论实现如下： </p>
            <ol>
                <li>线程 1 进入 <code>getInstance()</code> 方法。<br />
                <br />
                <li>由于 <code>instance</code> 为 <code>null</code>，线程 1 在 //1 处进入第一个 <code>synchronized</code> 块。 <br />
                <br />
                <li>局部变量 <code>inst</code> 获取 <code>instance</code> 的值，该值在 //2 处为 <code>null</code>。 <br />
                <br />
                <li>由于 <code>inst</code> 为 <code>null</code>，线程 1 在 //3 处进入第二个 <code>synchronized</code> 块。 <br />
                <br />
                <li>线程 1 然后开始执行 //4 处的代码，同时使 <code>inst</code> 为非 <code>null</code>，但在 <code>Singleton</code> 的构造函数执行前。（这就是我们刚才看到的无序写入问题。） <br />
                <br />
                <li>线程 1 被线程 2 预占。<br />
                <br />
                <li>线程 2 进入 <code>getInstance()</code> 方法。<br />
                <br />
                <li>由于 <code>instance</code> 为 <code>null</code>，线程 2 试图在 //1 处进入第一个 <code>synchronized</code> 块。由于线程 1 目前持有此锁，线程 2 被阻断。<br />
                <br />
                <li>线程 1 然后完成 //4 处的执行。<br />
                <br />
                <li>线程 1 然后将一个构造完整的 <code>Singleton</code> 对象在 //5 处赋值给变量 <code>instance</code>，并退出这两个 <code>synchronized</code> 块。 <br />
                <br />
                <li>线程 1 返回 <code>instance</code>。<br />
                <br />
                <li>然后执行线程 2 并在 //2 处将 <code>instance</code> 赋值给 <code>inst</code>。<br />
                <br />
                <li>线程 2 发现 <code>instance</code> 为非 <code>null</code>，将其返回。 </li>
            </ol>
            <p>这里的关键行是 //5。此行应该确保 <code>instance</code> 只为 <code>null</code> 或引用一个构造完整的 <code>Singleton</code> 对象。该问题发生在理论和实际彼此背道而驰的情况下。</p>
            <p>由于当前内存模型的定义，清单 7 中的代码无效。Java 语言规范（Java Language Specification，JLS）要求不能将 <code>synchronized</code> 块中的代码移出来。但是，并没有说不能将 <code>synchronized</code> 块外面的代码移<em>入</em> <code>synchronized</code> 块中。 </p>
            <p>JIT 编译器会在这里看到一个优化的机会。此优化会删除 //4 和 //5 处的代码，组合并且生成清单 8 中所示的代码。 </p>
            <br />
            <a name="code8"><strong>清单 8. 从清单 7 中优化来的代码。</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="57" sizcache="2">
                <tbody sizset="57" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">                        public static Singleton getInstance()
                        {
                        if (instance == null)
                        {
                        synchronized(Singleton.class) {      //1
                        Singleton inst = instance;         //2
                        if (inst == null)
                        {
                        synchronized(Singleton.class) {  //3
                        //inst = new Singleton();      //4
                        instance = new Singleton();
                        }
                        //instance = inst;               //5
                        }
                        }
                        }
                        return instance;
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>如果进行此项优化，您将同样遇到我们之前讨论过的无序写入问题。</p>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="58" sizcache="2">
                <tbody sizset="58" sizcache="1">
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="59" sizcache="2">
                <tbody sizset="60" sizcache="2">
                    <tr align="right" sizset="60" sizcache="2">
                        <td sizset="60" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0" sizset="60" sizcache="2">
                            <tbody sizset="60" sizcache="1">
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-dcl.html#main"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="5"><span class="atitle">用 volatile 声明每一个变量怎么样？</span></a></p>
            <p>另一个想法是针对变量 <code>inst</code> 以及 <code>instance</code> 使用关键字 <code>volatile</code>。根据 JLS（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-dcl.html#resources">参考资料</a>），声明成 <code>volatile</code> 的变量被认为是顺序一致的，即，不是重新排序的。但是试图使用 <code>volatile</code> 来修正双重检查锁定的问题，会产生以下两个问题：</p>
            <ul>
                <li>这里的问题不是有关顺序一致性的，而是代码被移动了，不是重新排序。<br />
                <br />
                <li>即使考虑了顺序一致性，大多数的 JVM 也没有正确地实现 <code>volatile</code>。 </li>
            </ul>
            <p>第二点值得展开讨论。假设有清单 9 中的代码：</p>
            <br />
            <a name="code9"><strong>清单 9. 使用了 volatile 的顺序一致性</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="61" sizcache="2">
                <tbody sizset="61" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">                        class test
                        {
                        private volatile boolean stop = false;
                        private volatile int num = 0;
                        public void foo()
                        {
                        num = 100;    //This can happen second
                        stop = true;  //This can happen first
                        //...
                        }
                        public void bar()
                        {
                        if (stop)
                        num += num;  //num can == 0!
                        }
                        //...
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>根据 JLS，由于 <code>stop</code> 和 <code>num</code> 被声明为 <code>volatile</code>，它们应该顺序一致。这意味着如果 <code>stop</code> 曾经是 <code>true</code>，<code>num</code> 一定曾被设置成 <code>100</code>。尽管如此，因为许多 JVM 没有实现 <code>volatile</code> 的顺序一致性功能，您就不能依赖此行为。因此，如果线程 1 调用 <code>foo</code> 并且线程 2 并发地调用 <code>bar</code>，则线程 1 可能在 <code>num</code> 被设置成为 <code>100</code> 之前将 <code>stop</code> 设置成 <code>true</code>。这将导致线程见到 <code>stop</code> 是 <code>true</code>，而 <code>num</code> 仍被设置成 <code>0</code>。使用 <code>volatile</code> 和 64 位变量的原子数还有另外一些问题，但这已超出了本文的讨论范围。有关此主题的更多信息，请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-dcl.html#resources">参考资料</a>。</p>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="62" sizcache="2">
                <tbody sizset="62" sizcache="1">
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="63" sizcache="2">
                <tbody sizset="64" sizcache="2">
                    <tr align="right" sizset="64" sizcache="2">
                        <td sizset="64" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0" sizset="64" sizcache="2">
                            <tbody sizset="64" sizcache="1">
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-dcl.html#main"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="6"><span class="atitle">解决方案</span></a></p>
            <p>底线就是：无论以何种形式，都不应使用双重检查锁定，因为您不能保证它在任何 JVM 实现上都能顺利运行。JSR-133 是有关内存模型寻址问题的，尽管如此，新的内存模型也不会支持双重检查锁定。因此，您有两种选择：</p>
            <ul>
                <li>接受如清单 2 中所示的 <code>getInstance()</code> 方法的同步。<br />
                <br />
                <li>放弃同步，而使用一个 <code>static</code> 字段。 </li>
            </ul>
            <p>选择项 2 如清单 10 中所示</p>
            <br />
            <a name="code10"><strong>清单 10. 使用 static 字段的单例实现</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="65" sizcache="2">
                <tbody sizset="65" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">                        class Singleton
                        {
                        private Vector v;
                        private boolean inUse;
                        private static Singleton instance = new Singleton();
                        private Singleton()
                        {
                        v = new Vector();
                        inUse = true;
                        //...
                        }
                        public static Singleton getInstance()
                        {
                        return instance;
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>清单 10 的代码没有使用同步，并且确保调用 <code>static getInstance()</code> 方法时才创建 <code>Singleton</code>。如果您的目标是消除同步，则这将是一个很好的选择。</p>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="66" sizcache="2">
                <tbody sizset="66" sizcache="1">
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="67" sizcache="2">
                <tbody sizset="68" sizcache="2">
                    <tr align="right" sizset="68" sizcache="2">
                        <td sizset="68" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0" sizset="68" sizcache="2">
                            <tbody sizset="68" sizcache="1">
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-dcl.html#main"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="7"><span class="atitle">String 不是不变的</span></a></p>
            <p>鉴于无序写入和引用在构造函数执行前变成非 <code>null</code> 的问题，您可能会考虑 <code>String</code> 类。假设有下列代码：</p>
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="69" sizcache="2">
                <tbody sizset="69" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">private String str;
                        //...
                        str = new String("hello");
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p><code>String</code> 类应该是不变的。尽管如此，鉴于我们之前讨论的无序写入问题，那会在这里导致问题吗？答案是肯定的。考虑两个线程访问 <code>String str</code>。一个线程能看见 <code>str</code> 引用一个 <code>String</code> 对象，在该对象中构造函数尚未运行。事实上，清单 11 包含展示这种情况发生的代码。注意，这个代码仅在我测试用的旧版 JVM 上会失败。IBM 1.3 和 Sun 1.3 JVM 都会如期生成不变的 <code>String</code>。</p>
            <br />
            <a name="code11"><strong>清单 11. 可变 String 的例子</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="70" sizcache="2">
                <tbody sizset="70" sizcache="1">
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">                        class StringCreator extends Thread
                        {
                        MutableString ms;
                        public StringCreator(MutableString muts)
                        {
                        ms = muts;
                        }
                        public void run()
                        {
                        while(true)
                        ms.str = new String("hello");          //1
                        }
                        }
                        class StringReader extends Thread
                        {
                        MutableString ms;
                        public StringReader(MutableString muts)
                        {
                        ms = muts;
                        }
                        public void run()
                        {
                        while(true)
                        {
                        if (!(ms.str.equals("hello")))         //2
                        {
                        System.out.println("String is not immutable!");
                        break;
                        }
                        }
                        }
                        }
                        class MutableString
                        {
                        public String str;                         //3
                        public static void main(String args[])
                        {
                        MutableString ms = new MutableString();  //4
                        new StringCreator(ms).start();           //5
                        new StringReader(ms).start();            //6
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>此代码在 //4 处创建一个 <code>MutableString</code> 类，它包含了一个 <code>String</code> 引用，此引用由 //3 处的两个线程共享。在行 //5 和 //6 处，在两个分开的线程上创建了两个对象 <code>StringCreator</code> 和 <code>StringReader</code>。传入一个 <code>MutableString</code> 对象的引用。<code>StringCreator</code> 类进入到一个无限循环中并且使用值&#8220;hello&#8221;在 //1 处创建 <code>String</code> 对象。<code>StringReader</code> 也进入到一个无限循环中，并且在 //2 处检查当前的 <code>String</code> 对象的值是不是 &#8220;hello&#8221;。如果不行，<code>StringReader</code> 线程打印出一条消息并停止。如果 <code>String</code> 类是不变的，则从此程序应当看不到任何输出。如果发生了无序写入问题，则使 <code>StringReader</code> 看到 <code>str</code> 引用的惟一方法绝不是值为&#8220;hello&#8221;的 <code>String</code> 对象。</p>
            <p>在旧版的 JVM 如 Sun JDK 1.2.1 上运行此代码会导致无序写入问题。并因此导致一个非不变的 <code>String</code>。</p>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="71" sizcache="2">
                <tbody sizset="71" sizcache="1">
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="72" sizcache="2">
                <tbody sizset="73" sizcache="2">
                    <tr align="right" sizset="73" sizcache="2">
                        <td sizset="73" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0" sizset="73" sizcache="2">
                            <tbody sizset="73" sizcache="1">
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-dcl.html#main"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="7"><span class="atitle">结束语</span></a></p>
            <p>为避免单例中代价高昂的同步，程序员非常聪明地发明了双重检查锁定习语。不幸的是，鉴于当前的内存模型的原因，该习语尚未得到广泛使用，就明显成为了一种不安全的编程结构。重定义脆弱的内存模型这一领域的工作正在进行中。尽管如此，即使是在新提议的内存模型中，双重检查锁定也是无效的。对此问题最佳的解决方案是接受同步或者使用一个 <code>static field</code>。 </p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/Todd/aggbug/318917.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2010-04-21 00:49 <a href="http://www.blogjava.net/Todd/archive/2010/04/21/318917.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】在Java与C程序间进行socket通信的讨论</title><link>http://www.blogjava.net/Todd/archive/2010/04/15/318405.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Thu, 15 Apr 2010 05:29:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2010/04/15/318405.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/318405.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2010/04/15/318405.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/318405.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/318405.html</trackback:ping><description><![CDATA[<br />
<br />
1. 背景 <br />
使用socket在Java程序与C程序间进行进程间通信。本文主要描述了在同C程序进行通信的Client端的Java实现功能。 <br />
<br />
1.1. 使用的语言 <br />
Client端：Java，JVM（JDK1.3） <br />
Server端：C，UNIX（Sun Solaris） <br />
<br />
1.2. 讨论范围 <br />
数据发送：只涉及到Java中int整型系列的讨论，包括byte，short，int。 <br />
数据接受：涉及到byte，short，int，long，float，double，char。 <br />
<br />
1.3.Java与C的数据类型的比较 <br />
Type Java C <br />
short 2-Byte 2-Byte <br />
int 4-Byte 4-Byte <br />
long 8-Byte 4-Byte <br />
float 4-Byte 4-Byte <br />
double 8-Byte 8-Byte <br />
boolean 1-bit N/A <br />
byte 1-Byte N/A <br />
char 2-Byte 1-Byte <br />
<br />
2. 实现 <br />
输出流：使用OutputStream流发送数据到C程序端。 <br />
输入流：使用DataInputStream流从C程序端接受数据 <br />
<br />
2.1. 数据发送 <br />
由于DataOutputStream流对于Java各个基本数据类型都相应地提供了&#8220;写&#8221;方法，如wrightShort和wrightInt等，因此当进行进程间通信（sockect通信）时，我们总是优先考虑使用DataOutputStream流。 <br />
下面我们对DataOutputStream流及其成员方法进行分析： <br />
<br />
2.1.1. DataOutputStream流 <br />
DataOutputStream流实现了接口DataOutput。 <br />
本文只讨论writeByte(int v)、writeShort(int v)和writeInt(int v)部分（这是因为我们需要发送的数据只涉及到int，short和byte，其它的long，double等则不在这里介绍），而且它们都有一个共同的特征，即唯一的int类型的输入参数。 <br />
这些成员方法的功能描述也为我们以后手动进行字节顺序转换，提供了理论依据。 <br />
<clk style="font-size: 12px"></clk>2.1.2. <nobr style="font-size: 12px; color: #6600ff; border-bottom: #6600ff 1px dotted; background-color: transparent; text-decoration: underline">网络</nobr>字节顺序 <br />
规定：网络上传输的数据统一采用Big Endian格式（即&#8220;高字节在前&#8221;），我们称之为&#8220;网络字节顺序&#8221;（network byte order）。 <br />
<br />
Big Endian格式： <br />
高字节 低字节 <br />
1 2 3 4 <br />
Byte[0] byte[1] byte[2] byte[3]输出缓冲区 <br />
<br />
因此，无论本机字节顺序采用的那种顺序，在发送到网络之前都要转化为网络字节顺序，才能进行传输。特别是在Java与C两种不同语言的应用程序间进行通信时，这一点优为重要。（若是两个Java程序间通信时可能只要保证接受与发送采用相同的字节顺序，则可以不进行转换格式，但这种做法并不好，不具有良好的移植性） <br />
<br />
2.1.3. 数据发送：手动字节转换 / writeInt方法 <br />
以writeInt(int v)为例进行描述： <br />
阅读DataOutput的writeInt(int v)方法的文档可知： <br />
使用writeInt方法可以写一个4-byte的int值v到输出流，其字节顺序为: <br />
<br />
(byte)(0xff &amp; (v &gt;&gt; 24)) byte[0] 高字节 <br />
(byte)(0xff &amp; (v &gt;&gt; 16)) byte[1] <br />
(byte)(0xff &amp; (v &gt;&gt; 8)) byte[2] <br />
(byte)(0xff &amp; v) byte[3] 低字节 <br />
这样的字节顺序为Big Endian格式，标准的&#8220;网络字节顺序&#8221;。 <br />
但是在实际工作中输出流采用DataOutputStream.readInt(int)方法时写数据出错，需要自己手动按照以上所说的对需要写的v值进行转换（通过移位完成），转换的代码如下所示，可参见程序SocketClient.java中的ByteConverter.intToByte()方法。 <br />
static public final byte[] intToByte( <br />
int value, int offset, int length, byte[] buffer) <br />
{ // High byte first on network <br />
for (int i=0,j=length-1; i&lt;length; i++,j--) { <br />
if ( j+offset &gt;= 0 &amp;&amp; j+offset &lt; 1024 ) { <br />
buffer[j+offset] = (byte)( (value &gt;&gt; i*8) &amp; 0xFF ); <br />
} else { <br />
System.out.println ( <br />
"Array index out of the bounds:Index=" + (j+offset) ); <br />
} <br />
} <br />
return buffer; <br />
} <br />
<br />
<br />
2.2. 数据接收 <br />
同数据发送相同，由于DataInputStream流对于Java各个基本数据类型都相应地提供了&#8220;读&#8221;方法，如readShort和readInt等，因此当进行进程间通信（sockect通信）时，我们总是优先考虑使用DataInputStream流。 <br />
而与数据发送不同的是，DataInputStream下的成员方法经实际测试，&#8220;基本上可以&#8221;根据数据类型正确读出相应的数值。 <br />
但并非完美，特别是与不同语言的应用程序进行通信时（如C）。 <br />
<br />
根据表1（Java与C的数据类型的比较）可知： <br />
(1)long型的字节数在Java和C中相差4个字节： <br />
因此由readLong方法读来的数值应进行带符号的右移32（4-byte）位才能得到在C程序中相应的long型数值。 <br />
Type Java C <br />
long 8-Byte 4-Byte <br />
<br />
(2)由于Java中的char型为2个字节，C中的char型为1个字节，因此不能使用readChar方法来读取C程序中的char数值。 <br />
然而在Java中byte型为1个字节长，因此可以使用readByte方法得到C程序中的char型数值。 <br />
Type Java C <br />
byte 1-Byte N/A <br />
char 2-Byte 1-Byte 
<img src ="http://www.blogjava.net/Todd/aggbug/318405.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2010-04-15 13:29 <a href="http://www.blogjava.net/Todd/archive/2010/04/15/318405.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】网络字节序与主机字节序</title><link>http://www.blogjava.net/Todd/archive/2010/04/15/318387.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Thu, 15 Apr 2010 02:37:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2010/04/15/318387.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/318387.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2010/04/15/318387.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/318387.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/318387.html</trackback:ping><description><![CDATA[<p>最近在项目开发过程中，需要在采用JAVA作为语言的服务器与采用C++作为语言的服务器间进行通信，这就涉及到这两种语言间数据类型的转换以及网络字节序与主机字节序的区别。该文主要说说网络字节序和主机字节序的区别以及Little endian与Big endian的概念。其实编程的事就比较简单了<br />
&nbsp;&nbsp; 我也懒得写了，直接引用了我觉得写的挺好的两篇<a onclick="javascript:tagshow(event, '%CE%C4%D5%C2');" href="javascript:;" target="_self"><u><strong>文章</strong></u></a>：<br />
</p>
<h1 style="font-weight: normal"><font size="3"><a href="http://blog.ednchina.com/qinyonglyz/194674/message.aspx#">什么是Big Endian和Little Endian？</a></font></h1>
<p>来源：http://blog.ednchina.com/qinyonglyz/194674/message.aspx<br />
</p>
<div>
<p><strong>1．故事的起源</strong></p>
<p>&#8220;endian&#8221;这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开，由此曾发生过六次叛乱，其中一个皇帝送了命，另一个丢了王位。</p>
<p>我们一般将endian翻译成&#8220;字节序&#8221;，将big endian和little endian称作&#8220;大尾&#8221;和&#8220;小尾&#8221;。</p>
<p><strong>2．什么是Big Endian和Little Endian？</strong></p>
<p>在设计计算机系统的时候，有两种处理内存中数据的方法。一种叫为little-endian，存放在内存中最低位的数值是来自数据的最右边部分（也就是数据的最低位部分）。比如一个16进制数字0x12345678，在内存存放的方式如下：</p>
<table cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td valign="top" width="74">
            <p>值</p>
            </td>
            <td valign="top" width="122">
            <p>0111，1000</p>
            </td>
            <td valign="top" width="109">
            <p>0101，0110</p>
            </td>
            <td valign="top" width="109">
            <p>0011，0100</p>
            </td>
            <td valign="top" width="122">
            <p>0001，0010</p>
            </td>
        </tr>
        <tr>
            <td valign="top" width="74">
            <p>地址</p>
            </td>
            <td valign="top" width="122">
            <p>100</p>
            </td>
            <td valign="top" width="109">
            <p>101</p>
            </td>
            <td valign="top" width="109">
            <p>102</p>
            </td>
            <td valign="top" width="122">
            <p>103</p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 另一种称为big-endian，正好相反，存放在内存中最低位的数值是来自数据的最左边边部分（也就是数据的最高为部分）。比如一个16进制数字0x12345678，在内存存放的方式如下：</p>
<table cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td valign="top" width="74">
            <p>值</p>
            </td>
            <td valign="top" width="122">
            <p>0001，0010</p>
            </td>
            <td valign="top" width="109">
            <p>0011，0100</p>
            </td>
            <td valign="top" width="109">
            <p>0101，0110</p>
            </td>
            <td valign="top" width="122">
            <p>0111，1000</p>
            </td>
        </tr>
        <tr>
            <td valign="top" width="74">
            <p>地址</p>
            </td>
            <td valign="top" width="122">
            <p>100</p>
            </td>
            <td valign="top" width="109">
            <p>101</p>
            </td>
            <td valign="top" width="109">
            <p>102</p>
            </td>
            <td valign="top" width="122">
            <p>103</p>
            </td>
        </tr>
    </tbody>
</table>
<p><font face="宋体">比如某些文件需要在不同平台处理，或者通过</font>Socket通信。这方面我们可以借助ntohl(), ntohs(), htonl(), and htons()函数进行格式转换。</p>
<p><strong>3．如何判断系统是Big Endian还是Little Endian？</strong></p>
<p>在/usr/include/中（包括子目录）查找字符串BYTE_ORDER(或_BYTE_ORDER, __BYTE_ORDER)，确定其值。这个值一般在endian.h或machine/endian.h文件中可以找到,有时在feature.h中，不同的<a onclick="javascript:tagshow(event, '%B2%D9%D7%F7%CF%B5%CD%B3');" href="javascript:;" target="_self"><u><strong>操作系统</strong></u></a>可能有所不同。一般来说，Little Endian系统BYTE_ORDER(或_BYTE_ORDER,__BYTE_ORDER)为1234，Big Endian系统为4321。大部分用户的操作系统（如windows, FreeBsd,Linux）是Little Endian的。少部分，如MAC OS ,是Big Endian 的。本质上说，Little Endian还是Big Endian与操作系统和芯片类型都有关系。</p>
<p><font size="2">======================================================================</font></p>
<p><font size="2">ext3 文件系统在硬盘分区上的数据是按照 Intel 的 Little-endian 格式存放的，如果是在 PC 以外的平台上开发 ext3 相关的程序，要特别注意这一点。</font></p>
<p><font face="courier new,courier,monospace" size="2">谈到字节序的问题，必然牵涉到两大 CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据，而x86系列则采用little endian方式存储数据。那么究竟什么是big endian，什么又是little endian呢？<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 其实big endian是指低地址存放最高有效字节（MSB），而little endian则是低地址存放最低有效字节（LSB）。</font></p>
<p><font face="courier new,courier,monospace" size="2">&nbsp;&nbsp;&nbsp;&nbsp; 用文字说明可能比较抽象，下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示：<br />
<br />
</font><font size="2"><font face="courier new,courier,monospace"><strong>Big&nbsp;Endian<br />
</strong><br />
&nbsp;&nbsp; 低地址&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 高地址<br />
&nbsp;&nbsp; -----------------------------------------&gt;<br />
&nbsp;&nbsp; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<br />
&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp; 12&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;34&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp; 78&nbsp;&nbsp;&nbsp; |<br />
&nbsp;&nbsp; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<br />
<br />
</font></font><font size="2"><font face="courier new,courier,monospace"><strong>Little Endian<br />
<br />
</strong>&nbsp;&nbsp; 低地址&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 高地址<br />
&nbsp;&nbsp; -----------------------------------------&gt;<br />
&nbsp;&nbsp; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<br />
&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;78&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;56&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;34&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;12&nbsp;&nbsp;&nbsp; |<br />
&nbsp;&nbsp; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 从上面两图可以看出，采用big endian方式存储数据是符合我们人类的思维习惯的。而little endian，!@#$%^&amp;*，见鬼去吧 -_-|||<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 为什么要注意字节序的问题呢？你可能这么问。当然，如果你写的程序只在单机环境下面运行，并且不和别人的程序打交道，那么你完全可以忽略字节序的存在。但是，如果你的程序要跟别人的程序产生交互呢？在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的，而 JAVA编写的程序则唯一采用big endian方式来存储数据。试想，如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果？就拿上面的 0x12345678来说，你的程序传递给别人的一个数据，将指向0x12345678的指针传给了JAVA程序，由于JAVA采取big endian方式存储数据，很自然的它会将你的数据翻译为0x78563412。什么？竟然变成另外一个数字了？是的，就是这种后果。因此，在你的C程序传给JAVA程序之前有必要进行字节序的转换<a onclick="javascript:tagshow(event, '%B9%A4%D7%F7');" href="javascript:;" target="_self"><u><strong>工作</strong></u></a>。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 无独有偶，所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时，在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。</font></font></p>
</div>
<p><br />
&nbsp;</p>
<h5 style="color: rgb(0,0,0)"><a href="http://www.cnblogs.com/jacktu/archive/2008/11/24/1339789.html">网络字节序与主机字节序</a></h5>
<p><br />
来源：http://www.cnblogs.com/jacktu/archive/2008/11/24/1339789.html<br />
不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序<br />
最常见的有两种<br />
1． Little endian：将低序字节存储在起始地址<br />
2． Big endian：将高序字节存储在起始地址<br />
<br />
LE little-endian<br />
最符合人的思维的字节序<br />
地址低位存储值的低位<br />
地址高位存储值的高位<br />
怎么讲是最符合人的思维的字节序，是因为从人的第一观感来说<br />
低位值小，就应该放在内存地址小的地方，也即内存地址低位<br />
反之，高位值就应该放在内存地址大的地方，也即内存地址高位<br />
<br />
BE big-endian<br />
最直观的字节序<br />
地址低位存储值的高位<br />
地址高位存储值的低位<br />
为什么说直观，不要考虑对应关系<br />
只需要把内存地址从左到右按照由低到高的顺序写出<br />
把值按照通常的高位到低位的顺序写出<br />
两者对照，一个字节一个字节的填充进去<br />
<br />
例子：在内存中双字0x01020304(DWORD)的存储方式<br />
<br />
内存地址<br />
4000 4001 4002 4003<br />
LE 04 03 02 01<br />
BE 01 02 03 04<br />
<br />
例子：如果我们将0x1234abcd写入到以0x0000开始的内存中，则结果为<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;big-endian&nbsp;&nbsp;little-endian<br />
0x0000&nbsp;&nbsp;0x12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0xcd<br />
0x0001&nbsp;&nbsp;0x23&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0xab<br />
0x0002&nbsp;&nbsp;0xab&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x34<br />
0x0003&nbsp;&nbsp;0xcd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x12<br />
x86系列CPU都是little-endian的字节序.<br />
<br />
网络字节顺序是TCP/IP中规定好的一种数据表示格式，它与具体的CPU类型、操作系统等无关，从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。<br />
<br />
为了进行转换 bsd socket提供了转换的函数 有下面四个<br />
htons 把unsigned short类型从主机序转换到网络序<br />
htonl 把unsigned long类型从主机序转换到网络序<br />
ntohs 把unsigned short类型从网络序转换到主机序<br />
ntohl 把unsigned long类型从网络序转换到主机序<br />
<br />
在使用little endian的系统中 这些函数会把字节序进行转换<br />
在使用big endian类型的系统中 这些函数会定义成空宏<br />
<br />
同样 在网络程序开发时 或是跨平台开发时 也应该注意保证只用一种字节序 不然两方的解释不一样就会产生bug.<br />
<br />
注：<br />
1、网络与主机字节转换函数:htons ntohs htonl ntohl (s 就是short l是long h是host n是network)<br />
2、不同的CPU上运行不同的操作系统，字节序也是不同的，参见下表。<br />
处理器&nbsp;&nbsp;&nbsp;&nbsp;操作系统&nbsp;&nbsp;&nbsp;&nbsp;字节排序<br />
Alpha&nbsp;&nbsp;&nbsp;&nbsp;全部&nbsp;&nbsp;&nbsp;&nbsp;Little endian<br />
HP-PA&nbsp;&nbsp;&nbsp;&nbsp;NT&nbsp;&nbsp;&nbsp;&nbsp;Little endian<br />
HP-PA&nbsp;&nbsp;&nbsp;&nbsp;UNIX&nbsp;&nbsp;&nbsp;&nbsp;Big endian<br />
Intelx86&nbsp;&nbsp;&nbsp;&nbsp;全部&nbsp;&nbsp;&nbsp;&nbsp;Little endian &lt;-----x86系统是小端字节序系统<br />
Motorola680x()&nbsp;&nbsp;&nbsp;&nbsp;全部&nbsp;&nbsp;&nbsp;&nbsp;Big endian<br />
MIPS&nbsp;&nbsp;&nbsp;&nbsp;NT&nbsp;&nbsp;&nbsp;&nbsp;Little endian<br />
MIPS&nbsp;&nbsp;&nbsp;&nbsp;UNIX&nbsp;&nbsp;&nbsp;&nbsp;Big endian<br />
PowerPC&nbsp;&nbsp;&nbsp;&nbsp;NT&nbsp;&nbsp;&nbsp;&nbsp;Little endian<br />
PowerPC&nbsp;&nbsp;&nbsp;&nbsp;非NT&nbsp;&nbsp;&nbsp;&nbsp;Big endian&nbsp;&nbsp;&lt;-----PPC系统是大端字节序系统<br />
RS/6000&nbsp;&nbsp;&nbsp;&nbsp;UNIX&nbsp;&nbsp;&nbsp;&nbsp;Big endian<br />
SPARC&nbsp;&nbsp;&nbsp;&nbsp;UNIX&nbsp;&nbsp;&nbsp;&nbsp;Big endian<br />
IXP1200 ARM核心&nbsp;&nbsp;&nbsp;&nbsp;全部&nbsp;&nbsp;&nbsp;&nbsp;Little endian<br />
=============================================<br />
字节序转换类：<br />
</p>
<p>/**<br />
* 通信格式转换<br />
*<br />
* <font style="background-color: #00ffff">Java</font>和一些windows编程语言如c、c++、delphi所写的网络程序进行通讯时，需要进行相应的转换<br />
* 高、低字节之间的转换<br />
* windows的字节序为低字节开头<br />
* linux,unix的字节序为高字节开头<br />
* <font style="background-color: #00ffff">java</font>则无论平台变化，都是高字节开头<br />
*/ </p>
<p>public class FormatTransfer {<br />
/**<br />
&nbsp; * 将int转为低字节在前，高字节在后的byte数组<br />
&nbsp; * @param n int<br />
&nbsp; * @return byte[]<br />
&nbsp; */<br />
public static byte[] toLH(int n) {<br />
&nbsp; byte[] b = new byte[4];<br />
&nbsp; b[0] = (byte) (n &amp; 0xff);<br />
&nbsp; b[1] = (byte) (n &gt;&gt; 8 &amp; 0xff);<br />
&nbsp; b[2] = (byte) (n &gt;&gt; 16 &amp; 0xff);<br />
&nbsp; b[3] = (byte) (n &gt;&gt; 24 &amp; 0xff);<br />
&nbsp; return b;<br />
} </p>
<p>/**<br />
&nbsp; * 将int转为高字节在前，低字节在后的byte数组<br />
&nbsp; * @param n int<br />
&nbsp; * @return byte[]<br />
&nbsp; */<br />
public static byte[] toHH(int n) {<br />
&nbsp; byte[] b = new byte[4];<br />
&nbsp; b[3] = (byte) (n &amp; 0xff);<br />
&nbsp; b[2] = (byte) (n &gt;&gt; 8 &amp; 0xff);<br />
&nbsp; b[1] = (byte) (n &gt;&gt; 16 &amp; 0xff);<br />
&nbsp; b[0] = (byte) (n &gt;&gt; 24 &amp; 0xff);<br />
&nbsp; return b;<br />
} </p>
<p>/**<br />
&nbsp; * 将short转为低字节在前，高字节在后的byte数组<br />
&nbsp; * @param n short<br />
&nbsp; * @return byte[]<br />
&nbsp; */<br />
public static byte[] toLH(short n) {<br />
&nbsp; byte[] b = new byte[2];<br />
&nbsp; b[0] = (byte) (n &amp; 0xff);<br />
&nbsp; b[1] = (byte) (n &gt;&gt; 8 &amp; 0xff);<br />
&nbsp; return b;<br />
} </p>
<p>/**<br />
&nbsp; * 将short转为高字节在前，低字节在后的byte数组<br />
&nbsp; * @param n short<br />
&nbsp; * @return byte[]<br />
&nbsp; */<br />
public static byte[] toHH(short n) {<br />
&nbsp; byte[] b = new byte[2];<br />
&nbsp; b[1] = (byte) (n &amp; 0xff);<br />
&nbsp; b[0] = (byte) (n &gt;&gt; 8 &amp; 0xff);<br />
&nbsp; return b;<br />
} </p>
<p>&nbsp;</p>
<p>/**<br />
&nbsp; * 将将int转为高字节在前，低字节在后的byte数组 </p>
<p>public static byte[] toHH(int number) {<br />
&nbsp; int temp = number;<br />
&nbsp; byte[] b = new byte[4];<br />
&nbsp; for (int i = b.length - 1; i &gt; -1; i--) {<br />
&nbsp;&nbsp;&nbsp; b = new Integer(temp &amp; 0xff).byteValue();<br />
&nbsp;&nbsp;&nbsp; temp = temp &gt;&gt; 8;<br />
&nbsp; }<br />
&nbsp; return b;<br />
} </p>
<p>public static byte[] IntToByteArray(int i) {<br />
&nbsp;&nbsp;&nbsp; byte[] abyte0 = new byte[4];<br />
&nbsp;&nbsp;&nbsp; abyte0[3] = (byte) (0xff &amp; i);<br />
&nbsp;&nbsp;&nbsp; abyte0[2] = (byte) ((0xff00 &amp; i) &gt;&gt; 8);<br />
&nbsp;&nbsp;&nbsp; abyte0[1] = (byte) ((0xff0000 &amp; i) &gt;&gt; 16);<br />
&nbsp;&nbsp;&nbsp; abyte0[0] = (byte) ((0xff000000 &amp; i) &gt;&gt; 24);<br />
&nbsp;&nbsp;&nbsp; return abyte0;<br />
} </p>
<p><br />
*/ </p>
<p>/**<br />
&nbsp; * 将float转为低字节在前，高字节在后的byte数组<br />
&nbsp; */<br />
public static byte[] toLH(float f) {<br />
&nbsp; return toLH(Float.floatToRawIntBits(f));<br />
} </p>
<p>/**<br />
&nbsp; * 将float转为高字节在前，低字节在后的byte数组<br />
&nbsp; */<br />
public static byte[] toHH(float f) {<br />
&nbsp; return toHH(Float.floatToRawIntBits(f));<br />
} </p>
<p>/**<br />
&nbsp; * 将String转为byte数组<br />
&nbsp; */<br />
public static byte[] stringToBytes(String s, int length) {<br />
&nbsp; while (s.getBytes().length &lt; length) {<br />
&nbsp;&nbsp;&nbsp; s += " ";<br />
&nbsp; }<br />
&nbsp; return s.getBytes();<br />
} </p>
<p><br />
/**<br />
&nbsp; * 将字节数组转换为String<br />
&nbsp; * @param b byte[]<br />
&nbsp; * @return String<br />
&nbsp; */<br />
public static String bytesToString(byte[] b) {<br />
&nbsp; StringBuffer result = new StringBuffer("");<br />
&nbsp; int length = b.length;<br />
&nbsp; for (int i=0; i&lt;length; i++) {<br />
&nbsp;&nbsp;&nbsp; result.append((<font style="background-color: #ffff00">char</font>)(b &amp; 0xff));<br />
&nbsp; }<br />
&nbsp; return result.toString();<br />
} </p>
<p>/**<br />
&nbsp; * 将字符串转换为byte数组<br />
&nbsp; * @param s String<br />
&nbsp; * @return byte[]<br />
&nbsp; */<br />
public static byte[] stringToBytes(String s) {<br />
&nbsp; return s.getBytes();<br />
} </p>
<p>/**<br />
&nbsp; * 将高字节数组转换为int<br />
&nbsp; * @param b byte[]<br />
&nbsp; * @return int<br />
&nbsp; */<br />
public static int hBytesToInt(byte[] b) {<br />
&nbsp; int s = 0;<br />
&nbsp; for (int i = 0; i &lt; 3; i++) {<br />
&nbsp;&nbsp;&nbsp; if (b &gt;= 0) {<br />
&nbsp;&nbsp;&nbsp; s = s + b;<br />
&nbsp;&nbsp;&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp; s = s + 256 + b;<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; s = s * 256;<br />
&nbsp; }<br />
&nbsp; if (b[3] &gt;= 0) {<br />
&nbsp;&nbsp;&nbsp; s = s + b[3];<br />
&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp; s = s + 256 + b[3];<br />
&nbsp; }<br />
&nbsp; return s;<br />
} </p>
<p>/**<br />
&nbsp; * 将低字节数组转换为int<br />
&nbsp; * @param b byte[]<br />
&nbsp; * @return int<br />
&nbsp; */<br />
public static int lBytesToInt(byte[] b) {<br />
&nbsp; int s = 0;<br />
&nbsp; for (int i = 0; i &lt; 3; i++) {<br />
&nbsp;&nbsp;&nbsp; if (b[3-i] &gt;= 0) {<br />
&nbsp;&nbsp;&nbsp; s = s + b[3-i];<br />
&nbsp;&nbsp;&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp; s = s + 256 + b[3-i];<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; s = s * 256;<br />
&nbsp; }<br />
&nbsp; if (b[0] &gt;= 0) {<br />
&nbsp;&nbsp;&nbsp; s = s + b[0];<br />
&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp; s = s + 256 + b[0];<br />
&nbsp; }<br />
&nbsp; return s;<br />
} </p>
<p><br />
/**<br />
&nbsp; * 高字节数组到short的转换<br />
&nbsp; * @param b byte[]<br />
&nbsp; * @return short<br />
&nbsp; */<br />
public static short hBytesToShort(byte[] b) {<br />
&nbsp; int s = 0;<br />
&nbsp; if (b[0] &gt;= 0) {<br />
&nbsp;&nbsp;&nbsp; s = s + b[0];<br />
&nbsp;&nbsp;&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp; s = s + 256 + b[0];<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; s = s * 256;<br />
&nbsp; if (b[1] &gt;= 0) {<br />
&nbsp;&nbsp;&nbsp; s = s + b[1];<br />
&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp; s = s + 256 + b[1];<br />
&nbsp; }<br />
&nbsp; short result = (short)s;<br />
&nbsp; return result;<br />
} </p>
<p>/**<br />
&nbsp; * 低字节数组到short的转换<br />
&nbsp; * @param b byte[]<br />
&nbsp; * @return short<br />
&nbsp; */<br />
public static short lBytesToShort(byte[] b) {<br />
&nbsp; int s = 0;<br />
&nbsp; if (b[1] &gt;= 0) {<br />
&nbsp;&nbsp;&nbsp; s = s + b[1];<br />
&nbsp;&nbsp;&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp; s = s + 256 + b[1];<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; s = s * 256;<br />
&nbsp; if (b[0] &gt;= 0) {<br />
&nbsp;&nbsp;&nbsp; s = s + b[0];<br />
&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp; s = s + 256 + b[0];<br />
&nbsp; }<br />
&nbsp; short result = (short)s;<br />
&nbsp; return result;<br />
} </p>
<p>/**<br />
&nbsp; * 高字节数组转换为float<br />
&nbsp; * @param b byte[]<br />
&nbsp; * @return float<br />
&nbsp; */<br />
public static float hBytesToFloat(byte[] b) {<br />
&nbsp; int i = 0;<br />
&nbsp; Float F = new Float(0.0);<br />
&nbsp; i = ((((b[0]&amp;0xff)&lt;&lt;8 | (b[1]&amp;0xff))&lt;&lt;8) | (b[2]&amp;0xff))&lt;&lt;8 | (b[3]&amp;0xff);<br />
&nbsp; return F.intBitsToFloat(i);<br />
} </p>
<p>/**<br />
&nbsp; * 低字节数组转换为float<br />
&nbsp; * @param b byte[]<br />
&nbsp; * @return float<br />
&nbsp; */<br />
public static float lBytesToFloat(byte[] b) {<br />
&nbsp; int i = 0;<br />
&nbsp; Float F = new Float(0.0);<br />
&nbsp; i = ((((b[3]&amp;0xff)&lt;&lt;8 | (b[2]&amp;0xff))&lt;&lt;8) | (b[1]&amp;0xff))&lt;&lt;8 | (b[0]&amp;0xff);<br />
&nbsp; return F.intBitsToFloat(i);<br />
} </p>
<p>/**<br />
&nbsp; * 将byte数组中的元素倒序排列<br />
&nbsp; */<br />
public static byte[] bytesReverseOrder(byte[] b) {<br />
&nbsp; int length = b.length;<br />
&nbsp; byte[] result = new byte[length];<br />
&nbsp; for(int i=0; i&lt;length; i++) {<br />
&nbsp;&nbsp;&nbsp; result[length-i-1] = b;<br />
&nbsp; }<br />
&nbsp; return result;<br />
} </p>
<p>/**<br />
&nbsp; * 打印byte数组<br />
&nbsp; */<br />
public static void printBytes(byte[] bb) {<br />
&nbsp; int length = bb.length;<br />
&nbsp; for (int i=0; i&lt;length; i++) {<br />
&nbsp;&nbsp;&nbsp; System.out.print(bb + " ");<br />
&nbsp; }<br />
&nbsp; System.out.println("");<br />
} </p>
<p>public static void logBytes(byte[] bb) {<br />
&nbsp; int length = bb.length;<br />
&nbsp; String ut = "";<br />
&nbsp; for (int i=0; i&lt;length; i++) {<br />
&nbsp;&nbsp;&nbsp; ut = out + bb + " ";<br />
&nbsp; } </p>
<p>} </p>
<p>/**<br />
&nbsp; * 将int类型的值转换为字节序颠倒过来对应的int值<br />
&nbsp; * @param i int<br />
&nbsp; * @return int<br />
&nbsp; */<br />
public static int reverseInt(int i) {<br />
&nbsp; int result = FormatTransfer.hBytesToInt(FormatTransfer.toLH(i));<br />
&nbsp; return result;<br />
} </p>
<p>/**<br />
&nbsp; * 将short类型的值转换为字节序颠倒过来对应的short值<br />
&nbsp; * @param s short<br />
&nbsp; * @return short<br />
&nbsp; */<br />
public static short reverseShort(short s) {<br />
&nbsp; short result = FormatTransfer.hBytesToShort(FormatTransfer.toLH(s));<br />
&nbsp; return result;<br />
} </p>
<p>/**<br />
&nbsp; * 将float类型的值转换为字节序颠倒过来对应的float值<br />
&nbsp; * @param f float<br />
&nbsp; * @return float<br />
&nbsp; */<br />
public static float reverseFloat(float f) {<br />
&nbsp; float result = FormatTransfer.hBytesToFloat(FormatTransfer.toLH(f));<br />
&nbsp; return result;<br />
} </p>
<p>}</p>
<img src ="http://www.blogjava.net/Todd/aggbug/318387.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2010-04-15 10:37 <a href="http://www.blogjava.net/Todd/archive/2010/04/15/318387.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ConcurrentHashMap应用注意事项</title><link>http://www.blogjava.net/Todd/archive/2010/04/13/318111.html</link><dc:creator>Todd</dc:creator><author>Todd</author><pubDate>Tue, 13 Apr 2010 01:23:00 GMT</pubDate><guid>http://www.blogjava.net/Todd/archive/2010/04/13/318111.html</guid><wfw:comment>http://www.blogjava.net/Todd/comments/318111.html</wfw:comment><comments>http://www.blogjava.net/Todd/archive/2010/04/13/318111.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Todd/comments/commentRss/318111.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Todd/services/trackbacks/318111.html</trackback:ping><description><![CDATA[1、public V get(Object key)不涉及到锁，也就是说获得对象时没有使用锁；<br />
2、keySet().iterator()及keys()，获取的Iterator、Enumeration变量是单线程访问安全的，多线程访问时要么生成多个Iterator、Enumeration(通过调用相应的获取方法)，要么以ConcurrentHashMap变量为锁进行同步(synchronized该变量)；ConcurrentHashMap变量是多线程访问安全的，尽管是多线程访问，多数情况下应该没有锁争用；<br />
3、put、remove方法要使用锁，但并不一定有锁争用，原因在于ConcurrentHashMap将缓存的变量分到多个Segment，每个Segment上有一个锁，只要多个线程访问的不是一个Segment就没有锁争用，就没有堵塞，各线程用各自的锁，ConcurrentHashMap缺省情况下生成16个Segment，也就是允许16个线程并发的更新而尽量没有锁争用；<br />
4、Iterator、Enumeration获得的对象，不一定是和其它更新线程同步，获得的对象可能是更新前的对象，ConcurrentHashMap允许一边更新、一边遍历，未遍历到的key一般能放映value更新；<br />
5、有些情况下这种不一致是允许的，如果需要最大的性能、吞吐量，则正好使用ConcurrentHashMap。 <br />
<br />
目前只想到能用于缓存无关紧要的信息，对于读写 都须同步的操作，竟然还要加synchronized，悲剧的线程安全
<img src ="http://www.blogjava.net/Todd/aggbug/318111.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Todd/" target="_blank">Todd</a> 2010-04-13 09:23 <a href="http://www.blogjava.net/Todd/archive/2010/04/13/318111.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>