??xml version="1.0" encoding="utf-8" standalone="yes"?>国产一区精品,91在线看黄,宅男宅女性影片资源在线1http://www.aygfsteel.com/Todd/category/44626.htmlC‘被遗忘者’的含义Q我们既非生者也非死者,我们被zȝ的和d的h遗忘?lt;br />我们回到了曾l告别的世界上,但是却永q无法回到我们曾l活着的那些日子,<br />永远无法回到那些我们曄p的h的n辏V我们是存在也是诅咒Q?lt;br />因此我们遗忘q去Qƈ且被q去遗忘... zh-cnWed, 19 Oct 2011 02:09:56 GMTWed, 19 Oct 2011 02:09:56 GMT60java heap spaceQ?PermGen space 错误 使用jvisualvm监测讄合理?/title><link>http://www.aygfsteel.com/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.aygfsteel.com/Todd/archive/2011/10/18/361543.html</guid><wfw:comment>http://www.aygfsteel.com/Todd/comments/361543.html</wfw:comment><comments>http://www.aygfsteel.com/Todd/archive/2011/10/18/361543.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/Todd/comments/commentRss/361543.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/Todd/services/trackbacks/361543.html</trackback:ping><description><![CDATA[使用myeclipse启动tomcat 报java heap space QPermGen space 错误Q分别ؓ heap内存不QPermGen内存不<br />需加大 tomcat启动参?Xmx ?nbsp;XX:MaxPermSize<br />PermGen是指内存的永久保存区域,它用于存放class?method 对象Q以及String 对象<br />Qsun原文Qpermanent 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>Q?br />PermGen又是一个特D内存区域:Classloader 加蝲的东东是不能回收的,它们攑֜PermGen?br />Qtomcat原文QWhy 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.Q?br /><br />回到我的问题来,打开%java_home%bin\jvisualvm.exe  (jdk6以上?<br />查看tomcat的内存情况,点击tomcat标签下monitor , 当used heap = max heap      used PermGen = max PermGen 时tomcatq在启动中,一会就报错Q?br />׃此前已经myeclipse中server->tomcat->tomcatx.x->jdk的参数设|成-Xms64m -Xmx512m -XX:MaxPermSize=80m<br />但是monitor 画面中max heap?256M<br />点击overview标签,发现jvm参数如下Q?br />-Xms64m<br />-Xmx512m<br />-XX:MaxPermSize=80m<br />-Xms64m<br />-Xmx256m<br /><br />昄tomcat使用了最后的讄-Xms64m -Xmx256mQ这个是在myeclipse的属?》java->installed jres 中的vm启动参数里,双击默认启用的jreQ把参数L或者设|成-Xms64m -Xmx512m<br />再回到server->tomcat->tomcatx.x->jdk中把-XX:MaxPermSize=80m修改?XX:MaxPermSize=128m<br />保存后再启动tomcatQok<br /><br />另外Q用jvisualvm持箋监测tomcat内存情况发现heap区域的内存用会在一个高峰后E_降低Q内存被回收了;<br />而PermGen区域的内存则是不断增加到达一个峰值后׃再增加,但之后此区的内存没有被回Ӟ验证了上面的说法Q?br /><br />多说两句Qjava的动态加载衍生出诸多框架Q在I间上也暴露出问题,反射在时间上也存在效率问题:下面是组试数据Q?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 /> 47 125 47 46 46<br /> average time = 66 ms.<br />Reference access to member field:<br /> 109 109 110 94 109<br /> average time = 106 ms.<br />Reflection access to member field:<br /> 13094 12984 13063 13062 13094<br /> 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 /> 47 31 109 109 31<br /> average time = 70 ms.<br />Direct call using passed value:<br /> 16 16 16 31 15<br /> average time = 20 ms.<br />Call to object using member field:<br /> 46 47 47 47 32<br /> average time = 43 ms.<br />Call to object using passed value:<br /> 15 16 31 16 16<br /> average time = 20 ms.<br />Reflection call using member field:<br /> 812 782 844 844 844<br /> average time = 829 ms.<br />Reflection call using passed value:<br /> 938 953 954 1031 953<br /> 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 /> 62 47 78 32 93<br /> average time = 63 ms.<br />Reflection Object creation:<br /> 125 94 94 109 187<br /> average time = 121 ms.<br />Direct byte[8] creation:<br /> 125 187 94 172 94<br /> average time = 137 ms.<br />Reflection byte[8] creation:<br /> 266 171 187 188 219<br /> average time = 191 ms.<br />Direct byte[64] creation:<br /> 250 172 156 125 203<br /> average time = 164 ms.<br />Reflection byte[64] creation:<br /> 281 219 203 203 219<br /> average time = 211 ms.</p>华丽的上层需要坚实的底层基础<br /><br /><a >http://wiki.apache.org/tomcat/FAQ/Deployment</a><br /><a >http://download.oracle.com/javase/7/docs/webnotes/tsg/TSG-VM/html/memleaks.html#gbyuu</a><br /><br /><img src ="http://www.aygfsteel.com/Todd/aggbug/361543.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/Todd/" target="_blank">Todd</a> 2011-10-18 22:16 <a href="http://www.aygfsteel.com/Todd/archive/2011/10/18/361543.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java反射效率试 jdk6http://www.aygfsteel.com/Todd/archive/2011/09/22/359221.htmlToddToddWed, 21 Sep 2011 23:18:00 GMThttp://www.aygfsteel.com/Todd/archive/2011/09/22/359221.htmlhttp://www.aygfsteel.com/Todd/comments/359221.htmlhttp://www.aygfsteel.com/Todd/archive/2011/09/22/359221.html#Feedback0http://www.aygfsteel.com/Todd/comments/commentRss/359221.htmlhttp://www.aygfsteel.com/Todd/services/trackbacks/359221.html

Java version 1.6.0_13
Java HotSpot(TM) Client VM
11.3-b02
Sun Microsystems Inc.

Direct access using member field:
 47 125 47 46 46
 average time = 66 ms.
Reference access to member field:
 109 109 110 94 109
 average time = 106 ms.
Reflection access to member field:
 13094 12984 13063 13062 13094
 average time = 13051 ms.

Java version 1.6.0_13
Java HotSpot(TM) Client VM
11.3-b02
Sun Microsystems Inc.

Direct call using member field:
 47 31 109 109 31
 average time = 70 ms.
Direct call using passed value:
 16 16 16 31 15
 average time = 20 ms.
Call to object using member field:
 46 47 47 47 32
 average time = 43 ms.
Call to object using passed value:
 15 16 31 16 16
 average time = 20 ms.
Reflection call using member field:
 812 782 844 844 844
 average time = 829 ms.
Reflection call using passed value:
 938 953 954 1031 953
 average time = 973 ms.

Java version 1.6.0_13
Java HotSpot(TM) Client VM
11.3-b02
Sun Microsystems Inc.

Direct Object creation:
 62 47 78 32 93
 average time = 63 ms.
Reflection Object creation:
 125 94 94 109 187
 average time = 121 ms.
Direct byte[8] creation:
 125 187 94 172 94
 average time = 137 ms.
Reflection byte[8] creation:
 266 171 187 188 219
 average time = 191 ms.
Direct byte[64] creation:
 250 172 156 125 203
 average time = 164 ms.
Reflection byte[64] creation:
 281 219 203 203 219
 average time = 211 ms.

 



Todd 2011-09-22 07:18 发表评论
]]>
eclipse wtp tomcat webservicehttp://www.aygfsteel.com/Todd/archive/2011/05/06/349652.htmlToddToddThu, 05 May 2011 17:45:00 GMThttp://www.aygfsteel.com/Todd/archive/2011/05/06/349652.htmlhttp://www.aygfsteel.com/Todd/comments/349652.htmlhttp://www.aygfsteel.com/Todd/archive/2011/05/06/349652.html#Feedback0http://www.aygfsteel.com/Todd/comments/commentRss/349652.htmlhttp://www.aygfsteel.com/Todd/services/trackbacks/349652.html1.what's eclipse wtp

wtp(web tools platform )目在eclipseq_上进行扩展,是一个开发j2ee web应用E序的工具集。wtp包含以下工具Q?/p>

* 一个源码编辑器www.ssbbww.com用来~辑html, javascript, css, jsp, sql, xml, dtd, xsd, 和wsdl?/p>

* 一个图形编辑器用来~辑xsd与wsdl?/p>

* j2ee目构徏器和一个j2ee向导工具?/p>

* 一个web服务创徏向导和管理器Q和ws-i 试工具?/p>

* 一个数据库讉KQ查询工L?/p>

wtp׃个子目构成:wst(web标准工具? 与jst(j2ee标准工具?8ttt8 wtp是什么意?是什么东西_什么叫-什么是wtp

wtp支付愿意原则Q用于分析社会成员ؓ目所产出的效益愿意支付的价倹{?/p>



2.安装及工E创建发布:eclipse for java ee 里已l安装wtp,so不用׃动手Qeclipse l典版安装wtp?
工程的创建及发布
见【传送门】:http://www.vogella.de/articles/EclipseWTP/article.html

3.wtp{疑见【传送门?a >http://wiki.eclipse.org/WTP_Tomcat_FAQ

4.接着创徏一个xfire webservice

?http://blog.csdn.net/xzmyzy/archive/2008/01/16/2047244.aspx
http://blog.csdn.net/alex197963/archive/2009/07/20/4363328.aspx

Todd 2011-05-06 01:45 发表评论
]]>
jadclipse 使用http://www.aygfsteel.com/Todd/archive/2011/03/10/346074.htmlToddToddThu, 10 Mar 2011 05:28:00 GMThttp://www.aygfsteel.com/Todd/archive/2011/03/10/346074.htmlhttp://www.aygfsteel.com/Todd/comments/346074.htmlhttp://www.aygfsteel.com/Todd/archive/2011/03/10/346074.html#Feedback0http://www.aygfsteel.com/Todd/comments/commentRss/346074.htmlhttp://www.aygfsteel.com/Todd/services/trackbacks/346074.html2.http://www.varaneckas.com/jad      下蝲jad.exe,攑ֈ一个文件夹下,路径如d:\temp\jad.exeQ?br />3.在eclipse->window->preferences->java->jadclipse      右侧 [path to decompiler] 输入 d:\temp\jad.exe
4.eclipse->window->General->Editors->file associations      右侧 [file types] ->add   输入 *.class ->ok
   选中新加?.class,下面的[associated editors] 选中[jadclipse class file viewer] -> default ->ok 
   重启 eclipse;打开一个class文gQ即可查看反~译代码Q欧?img src ="http://www.aygfsteel.com/Todd/aggbug/346074.html" width = "1" height = "1" />

Todd 2011-03-10 13:28 发表评论
]]>
汉字字符~码与{码详?/title><link>http://www.aygfsteel.com/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.aygfsteel.com/Todd/archive/2010/05/23/321656.html</guid><wfw:comment>http://www.aygfsteel.com/Todd/comments/321656.html</wfw:comment><comments>http://www.aygfsteel.com/Todd/archive/2010/05/23/321656.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/Todd/comments/commentRss/321656.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/Todd/services/trackbacks/321656.html</trackback:ping><description><![CDATA[E序中有汉字参数Q经怼遇到~码转码问题Qȝ下: <br /> <strong>1.汉字为多字节字符Q须多字节编码解?/strong>Q如"试".getBytes("gb2312"); <br /> <br /> q样"试".getBytes("gb2312")变成一个byte数组Q这时候你可以随意重新指定~码如iso-8859-1, <br /> String s1=new String("试".getBytes("gb2312"),"iso-8859-1"); <br /> <br /> ~ؓs1,q是s1变成一个是iso-8859-1~码的字W串Q如果你想重新{Z文,那么Q你用什么字W集~码的,必须用什么字W集来解 码,q里是iso-8859-1,可以q么来做 <br /> <br /> String s2 = new String(s1.getBytes("ISO-8859-1"),"gb2312"); <br /> <br /> <br /> q样s2又重新变回中文了Q所以当你打印s2Ӟ是“试”? <br /> <br /> <br /> <strong>2.用iso-8859-1做中间编码,</strong>原因Q? <br /> <br /> [1]iso-8859-1是单字节字符~码Q? <br /> <br /> [2]ANSI ~码 (如:GB2312, BIG5,Shift_JIS,ISO-8859-2{等Q,是多字节~码Q英文单字节Q中文多字节Q; <br /> <br /> [3]UNICODE ~码QUTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig....Q?是宽字节~码Q所有字W均是多字节Q? <br /> <br /> 因此用iso-8859-1做中间码Q会保持原有字节的秩序,不发生؜乱;可以理解为其他的~码对iso-8859-1兼容吧? <br /> <br /> 因此Q我们常怋?bytes = string.getBytes("iso-8859-1") 坐中间码来进行逆向操作Q得到原始的“字节?#8221;。然后再使用正确的ANSI ~码Q比?string = new String(bytes, "GB2312")Q来得到正确?#8220;UNICODE 字符?#8221;? <br /> <br /> 不信的话可以试试Qutf8和gb不能互相转换Q只有iso-8859-1做中间码可以完美互相转码Q!Q? <img src ="http://www.aygfsteel.com/Todd/aggbug/321656.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/Todd/" target="_blank">Todd</a> 2010-05-23 13:19 <a href="http://www.aygfsteel.com/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.aygfsteel.com/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.aygfsteel.com/Todd/archive/2010/05/23/321655.html</guid><wfw:comment>http://www.aygfsteel.com/Todd/comments/321655.html</wfw:comment><comments>http://www.aygfsteel.com/Todd/archive/2010/05/23/321655.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/Todd/comments/commentRss/321655.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/Todd/services/trackbacks/321655.html</trackback:ping><description><![CDATA[一Q问? <br /> 1.多参数回? <br /> 2.参数传递出Cؕ? <br /> <br /> 二,解决 <br /> 1.使用byte[]数组传入c++,在生成的头文仉׃变成jbyteArray cd <br /> 例如Qjava 中参敎ͼbyte[]account,头文仉参数变成jbyteArray accountQ? <br /> 通过c++修改完account的值后Qjava要获取该|直接使用 <br /> jbyte* jbAccount = (env)->GetByteArrayElements(env, account, 0); <br /> char* szAccount = (char*)jbAccount; <br /> 指针的地址q不是account的地址Q最后赋下值才? <br /> env->SetByteArrayRegion(account,0,strlen(szAccount),jbyte* jbAccount); <br /> <br /> 2.String.getBytes()生成的byte数组传入c++后,在字W串的结会有多余ؕ码, <br /> 解决办法是,传入byte数组Ӟ把数l的长度lengthQ也传入c++, <br /> 令接受的数组strBuff[length]='\0';卛_解决问题 <img src ="http://www.aygfsteel.com/Todd/aggbug/321655.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/Todd/" target="_blank">Todd</a> 2010-05-23 13:18 <a href="http://www.aygfsteel.com/Todd/archive/2010/05/23/321655.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java dom4j操作xmlhttp://www.aygfsteel.com/Todd/archive/2010/05/22/321618.htmlToddToddSat, 22 May 2010 10:26:00 GMThttp://www.aygfsteel.com/Todd/archive/2010/05/22/321618.htmlhttp://www.aygfsteel.com/Todd/comments/321618.htmlhttp://www.aygfsteel.com/Todd/archive/2010/05/22/321618.html#Feedback0http://www.aygfsteel.com/Todd/comments/commentRss/321618.htmlhttp://www.aygfsteel.com/Todd/services/trackbacks/321618.html阅读全文

Todd 2010-05-22 18:26 发表评论
]]>
汉字字符~码与{码详?/title><link>http://www.aygfsteel.com/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.aygfsteel.com/Todd/archive/2010/04/26/319387.html</guid><wfw:comment>http://www.aygfsteel.com/Todd/comments/319387.html</wfw:comment><comments>http://www.aygfsteel.com/Todd/archive/2010/04/26/319387.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/Todd/comments/commentRss/319387.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/Todd/services/trackbacks/319387.html</trackback:ping><description><![CDATA[E序中有汉字参数Q经怼遇到~码转码问题Qȝ下:<br /> 1.汉字为多字节字符Q须多字节编码解码,?试".getBytes("gb2312");<br /> <pre>q样"试".getBytes("gb2312")变成一个byte数组Q这时候你可以随意重新指定~码如iso-8859-1,<br /> <br /> String s1=new String("试".getBytes("gb2312"),"iso-8859-1");<br /> <br /> ~ؓs1,q是s1变成一个是iso-8859-1~码的字W串Q如果你想重新{Z文,那么Q你用什么字W集~码的,必须用什么字W集来解码,q里是iso-8859-1,<br /> <br /> 可以q么来做<br /> <br /> <br /> <br /> String s2 = new String(s1.getBytes("ISO-8859-1"),"gb2312");<br /> <br /> q样s2又重新变回中文了Q所以当你打印s2Ӟ是“试”?br /> <br /> <br /> <br /> 2.用iso-8859-1做中间编码,原因Q?br /> [1]iso-8859-1是单字节字符~码Q?br /> [2]ANSI ~码 (如:GB2312, BIG5,Shift_JIS,ISO-8859-2{等Q,是多字节~码Q英文单字节Q中文多字节Q;<br /> [3]UNICODE ~码QUTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig....Q?是宽字节~码Q所有字W均是多字节Q?br /> 因此用iso-8859-1做中间码Q会保持原有字节的秩序,不发生؜乱;可以理解为其他的~码对iso-8859-1兼容吧?br /> <br /> 因此Q我们常怋?bytes = string.getBytes("iso-8859-1") 坐中间码来进行逆向操作Q得到原始的“字节?#8221;。然后再使用正确?br /> ANSI ~码Q比?string = new String(bytes, "GB2312")Q来得到正确?#8220;UNICODE 字符?#8221;?br /> <br /> 不信的话可以试试Qutf8和gb不能互相转换Q只有iso-8859-1做中间码可以完美互相转码Q!Q?br /> </pre> <br /> <br /> <img src ="http://www.aygfsteel.com/Todd/aggbug/319387.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/Todd/" target="_blank">Todd</a> 2010-04-26 14:03 <a href="http://www.aygfsteel.com/Todd/archive/2010/04/26/319387.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>四个有用的过虑器http://www.aygfsteel.com/Todd/archive/2010/04/24/319270.htmlToddToddSat, 24 Apr 2010 08:36:00 GMThttp://www.aygfsteel.com/Todd/archive/2010/04/24/319270.htmlhttp://www.aygfsteel.com/Todd/comments/319270.htmlhttp://www.aygfsteel.com/Todd/archive/2010/04/24/319270.html#Feedback0http://www.aygfsteel.com/Todd/comments/commentRss/319270.htmlhttp://www.aygfsteel.com/Todd/services/trackbacks/319270.htmlimport javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 用于的 Browser 不缓存页面的qo?br />  */
public class ForceNoCacheFilter implements Filter {

 public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException
 {
  ((HttpServletResponse) response).setHeader("Cache-Control","no-cache");
  ((HttpServletResponse) response).setHeader("Pragma","no-cache");
  ((HttpServletResponse) response).setDateHeader ("Expires", -1);
  filterChain.doFilter(request, response);
 }

 public void destroy()
 {
 }

    public void init(FilterConfig filterConfig) throws ServletException
 {
 }
}

二、检用h否登陆的qo?/p>

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.io.IOException;

/**
 * 用于用h否登陆的qo器,如果未登录,则重定向到指的登录页?lt;p>
 * 配置参数<p>
 * checkSessionKey 需查的?Session 中保存的关键?lt;br/>
 * redirectURL 如果用户未登录,则重定向到指定的面QURL不包?ContextPath<br/>
 * notCheckURLList 不做查的URL列表Q以分号分开Qƈ?URL 中不包括 ContextPath<br/>
 */
public class CheckLoginFilter
 implements Filter
{
    protected FilterConfig filterConfig = null;
    private String redirectURL = null;
    private List notCheckURLList = new ArrayList();
    private String sessionKey = null;

 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException
 {
  HttpServletRequest request = (HttpServletRequest) servletRequest;
  HttpServletResponse response = (HttpServletResponse) servletResponse;

   HttpSession session = request.getSession();
  if(sessionKey == null)
  {
   filterChain.doFilter(request, response);
   return;
  }
  if((!checkRequestURIIntNotFilterList(request)) && session.getAttribute(sessionKey) == null)
  {
   response.sendRedirect(request.getContextPath() + redirectURL);
   return;
  }
  filterChain.doFilter(servletRequest, servletResponse);
 }

 public void destroy()
 {
  notCheckURLList.clear();
 }

 private boolean checkRequestURIIntNotFilterList(HttpServletRequest request)
 {
  String uri = request.getServletPath() + (request.getPathInfo() == null ? "" : request.getPathInfo());
  return notCheckURLList.contains(uri);
 }

 public void init(FilterConfig filterConfig) throws ServletException
 {
  this.filterConfig = filterConfig;
  redirectURL = filterConfig.getInitParameter("redirectURL");
  sessionKey = filterConfig.getInitParameter("checkSessionKey");

  String notCheckURLListStr = filterConfig.getInitParameter("notCheckURLList");

  if(notCheckURLListStr != null)
  {
   StringTokenizer st = new StringTokenizer(notCheckURLListStr, ";");
   notCheckURLList.clear();
   while(st.hasMoreTokens())
   {
    notCheckURLList.add(st.nextToken());
   }
  }
 }
}

三、字W编码的qo?/p>

import javax.servlet.*;
import java.io.IOException;

/**
 * 用于讄 HTTP h字符~码的过滤器Q通过qo器参数encoding指明使用何种字符~码,用于处理Html Formh参数的中文问?br />  */
public class CharacterEncodingFilter
 implements Filter
{
 protected FilterConfig filterConfig = null;
 protected String encoding = "";

 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException
 {
        if(encoding != null)
         servletRequest.setCharacterEncoding(encoding);
        filterChain.doFilter(servletRequest, servletResponse);
 }

 public void destroy()
 {
  filterConfig = null;
  encoding = null;
 }

    public void init(FilterConfig filterConfig) throws ServletException
 {
         this.filterConfig = filterConfig;
        this.encoding = filterConfig.getInitParameter("encoding");

 }
}

四、资源保护过滤器

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.
*

* It should be configured inside the web.xml. * * @author Derek Y. Shen */ 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) && !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) { //处理用户d /* UserBean user = (UserBean)req.getSession().getAttribute(BeanNames.USER_BEAN); if (user != null && user.getLoggedIn()) { //user logged in return true; } else { return false; }*/ } }



Todd 2010-04-24 16:36 发表评论
]]>
【{】单例模式不可用双重检查锁?/title><link>http://www.aygfsteel.com/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.aygfsteel.com/Todd/archive/2010/04/21/318917.html</guid><wfw:comment>http://www.aygfsteel.com/Todd/comments/318917.html</wfw:comment><comments>http://www.aygfsteel.com/Todd/archive/2010/04/21/318917.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/Todd/comments/commentRss/318917.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/Todd/services/trackbacks/318917.html</trackback:ping><description><![CDATA[<br /> 转自Qhttp://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> </h1> <h1>双重查锁定及单例模式</h1> <p id="subtitle"><em>全面理解q一失效的编E习?/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="http://www.ibm.com/i/c.gif" width="8" height="1" alt="" /></td><td width="16"><img alt="打印机的版面设|成横向打印模式" height="16" 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>'); //--> </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="所有的~程语言都有一些共用的习语。了解和使用一些习语很有用Q程序员们花费宝늚旉来创建、学习和实现q些习语。问题是Q稍后经q证明,一些习语ƈ不完全如其所声称的那P或者仅仅是与描q的功能不符。Java ~程语言也不例外Q其中的双重查锁定就是这L一个绝不应该用的习语。在本文中,Peter Haggar 介绍了双重检查锁定习语的渊源Q开发它的原因和它失效的原因? 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="http://www.ibm.com/i/c.gif" width="8" height="1" alt="" /></td><td width="16"><img src="http://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="http://www.ibm.com/i/c.gif" /></td> <td width="16"><img alt="" width="16" height="16" src="http://www.ibm.com/i/c.gif" /></td> <td class="small" width="122"> <p><span id="wmqeeuq" class="ast">未显C需?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)" ><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>U别Q?中</p> <p><a >Peter Haggar</a>, 高软g工程? IBM<br /> </p> <p>2004 q?5 ?01 ?/p> <blockquote>所有的~程语言都有一些共用的习语。了解和使用一些习语很有用Q程序员们花费宝늚旉来创建、学习和实现q些习语。问题是Q稍后经q证明,一些习语ƈ不完全如其所声称的那P或者仅仅是与描q的功能不符。在 Java ~程语言中,双重查锁定就是这L一个绝不应该用的习语。在本文中,Peter Haggar 介绍了双重检查锁定习语的渊源Q开发它的原因和它失效的原因?</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>Q本文在针对 Java 5.0 修订前参考了 Java 内存模型Q关于内存排序的描述也许不再正确。尽如此,在新的内存模型中Q双重检查锁定习语仍旧是无效的?/em> </p> <p>单例创徏模式是一个通用的编E习语。和多线E一起用时Q必需使用某种cd的同步。在努力创徏更有效的代码ӞJava E序员们创徏了双重检查锁定习语,其和单例创建模式一起用,从而限制同步代码量。然而,׃一些不太常见的 Java 内存模型l节的原因,q不能保证这个双重检查锁定习语有效。它偶尔会失败,而不是d败。此外,它失败的原因q不明显Q还包含 Java 内存模型的一些隐U细节。这些事实将D代码p|Q原因是双重查锁定难于跟t。在本文余下的部分里Q我们将详细介绍双重查锁定习语,从而理解它在何处失效?/p> <p><a name="1"><span id="wmqeeuq" class="atitle">单例创徏习语</span></a></p> <p>要理解双重检查锁定习语是从哪里v源的Q就必须理解通用单例创徏习语Q如清单 1 中的阐释Q?/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>Q?code>getInstance()</code> Ҏ只创Z个对象。这个实现适合于单U程E序。然而,当引入多U程Ӟ必通过同步来保?<code>getInstance()</code> Ҏ。如果不保护 <code>getInstance()</code> ҎQ则可能q回 <code>Singleton</code> 对象的两个不同的实例。假设两个线Eƈ发调?<code>getInstance()</code> Ҏq且按以下顺序执行调用: </p> <ol> <li>U程 1 调用 <code>getInstance()</code> Ҏq决?<code>instance</code> ?//1 处ؓ <code>null</code>?<br /> <br /> <li>U程 1 q入 <code>if</code> 代码块,但在执行 //2 处的代码行时被线E?2 预占?<br /> <br /> <li>U程 2 调用 <code>getInstance()</code> Ҏq在 //1 处决?<code>instance</code> ?<code>null</code>?<br /> <br /> <li>U程 2 q入 <code>if</code> 代码块ƈ创徏一个新?<code>Singleton</code> 对象q在 //2 处将变量 <code>instance</code> 分配l这个新对象?<br /> <br /> <li>U程 2 ?//3 处返?<code>Singleton</code> 对象引用?br /> <br /> <li>U程 2 被线E?1 预占?<br /> <br /> <li>U程 1 在它停止的地方启动,q执?//2 代码行,q导致创建另一?<code>Singleton</code> 对象?<br /> <br /> <li>U程 1 ?//3 处返回这个对象?</li> </ol> <p>l果?<code>getInstance()</code> Ҏ创徏了两?<code>Singleton</code> 对象Q而它本该只创Z个对象。通过同步 <code>getInstance()</code> Ҏ从而在同一旉只允怸个线E执行代码,q个问题得以ҎQ如清单 2 所C: </p> <br /> <a name="code2"><strong>清单 2. U程安全?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 中的代码针对多线E访?<code>getInstance()</code> Ҏq行得很好。然而,当分析这D代码时Q您会意识到只有在第一ơ调用方法时才需要同步。由于只有第一ơ调用执行了 //2 处的代码Q而只有此行代码需要同步,因此无需对后l调用用同步。所有其他调用用于决?<code>instance</code> 是非 <code>null</code> 的,q将其返回。多U程能够安全q发地执行除W一ơ调用外的所有调用。尽如此,׃该方法是 <code>synchronized</code> 的,需要ؓ该方法的每一ơ调用付出同步的代hQ即使只有第一ơ调用需要同步?</p> <p>Z此方法更为有效,一个被UCؓ双重查锁定的习语应q而生了。这个想法是Z避免寚wW一ơ调用外的所有调用都实行同步的昂贵代仗同步的代h在不同的 JVM 间是不同的。在早期Q代L当高。随着更高U的 JVM 的出玎ͼ同步的代价降低了Q但出入 <code>synchronized</code> Ҏ或块仍然有性能损失。不考虑 JVM 技术的q步Q程序员们绝不想不必要地费处理旉?/p> <p>因ؓ只有清单 2 中的 //2 行需要同步,我们可以只将其包装到一个同步块中,如清?3 所C: </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 中的代码展示了用多线E加以说明的和清?1 相同的问题。当 <code>instance</code> ?<code>null</code> Ӟ两个U程可以q发地进?<code>if</code> 语句内部。然后,一个线E进?<code>synchronized</code> 块来初始?<code>instance</code>Q而另一个线E则被阻断。当W一个线E退?<code>synchronized</code> 块时Q等待着的线E进入ƈ创徏另一?<code>Singleton</code> 对象。注意:当第二个U程q入 <code>synchronized</code> 块时Q它q没有检?<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" ><strong>回页?/strong></a></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="2"><span id="wmqeeuq" class="atitle">双重查锁?/span></a></p> <p>为处理清?3 中的问题Q我们需要对 <code>instance</code> q行W二ơ检查。这是“双重查锁?#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 处的W二ơ检查Q如清单 3 中那P创徏两个不同?<code>Singleton</code> 对象成ؓ不可能。假设有下列事g序列Q?</p> <ol> <li>U程 1 q入 <code>getInstance()</code> Ҏ?<br /> <br /> <li>׃ <code>instance</code> ?<code>null</code>Q线E?1 ?//1 处进?<code>synchronized</code> 块?<br /> <br /> <li>U程 1 被线E?2 预占?br /> <br /> <li>U程 2 q入 <code>getInstance()</code> Ҏ?br /> <br /> <li>׃ <code>instance</code> 仍旧?<code>null</code>Q线E?2 试图获取 //1 处的锁。然而,׃U程 1 持有该锁Q线E?2 ?//1 处阻塞?br /> <br /> <li>U程 2 被线E?1 预占?br /> <br /> <li>U程 1 执行Q由于在 //2 处实例仍旧ؓ <code>null</code>Q线E?1 q创Z?<code>Singleton</code> 对象q将其引用赋值给 <code>instance</code>?br /> <br /> <li>U程 1 退?<code>synchronized</code> 块ƈ?<code>getInstance()</code> Ҏq回实例?<br /> <br /> <li>U程 1 被线E?2 预占?br /> <br /> <li>U程 2 获取 //1 处的锁ƈ?<code>instance</code> 是否?<code>null</code>?<br /> <br /> <li>׃ <code>instance</code> 是非 <code>null</code> 的,q没有创建第二个 <code>Singleton</code> 对象Q由U程 1 创徏的对象被q回?</li> </ol> <p>双重查锁定背后的理论是完的。不q地是,现实完全不同。双重检查锁定的问题是:q不能保证它会在单处理器或多处理器计机上顺利运行?/p> <p>双重查锁定失败的问题q不归咎?JVM 中的实现 bugQ而是归咎?Java q_内存模型。内存模型允许所谓的“无序写入”Q这也是q些习语p|的一个主要原因?/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" ><strong>回页?/strong></a></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="3"><span id="wmqeeuq" class="atitle">无序写入</span></a></p> <p>释该问题Q需要重新考察上述清单 4 中的 //3 行。此行代码创Z一?<code>Singleton</code> 对象q初始化变量 <code>instance</code> 来引用此对象。这行代码的问题是:?<code>Singleton</code> 构造函C执行之前Q变?<code>instance</code> 可能成ؓ?<code>null</code> 的?/p> <p>什么?q一说法可能让您始料未及Q但事实实如此。在解释q个现象如何发生前,请先暂时接受q一事实Q我们先来考察一下双重检查锁定是如何被破坏的。假设清?4 中代码执行以下事件序列:</p> <ol> <li>U程 1 q入 <code>getInstance()</code> Ҏ?br /> <br /> <li>׃ <code>instance</code> ?<code>null</code>Q线E?1 ?//1 处进?<code>synchronized</code> 块?<br /> <br /> <li>U程 1 前进?//3 处,但在构造函数执?em>之前</em>Q实例成ؓ?<code>null</code>?<br /> <br /> <li>U程 1 被线E?2 预占?br /> <br /> <li>U程 2 查实例是否ؓ <code>null</code>。因为实例不?nullQ线E?2 ?<code>instance</code> 引用q回l一个构造完整但部分初始化了?<code>Singleton</code> 对象?<br /> <br /> <li>U程 2 被线E?1 预占?br /> <br /> <li>U程 1 通过q行 <code>Singleton</code> 对象的构造函数ƈ引用返回给它,来完成对该对象的初始化?</li> </ol> <p>此事件序列发生在U程 2 q回一个尚未执行构造函数的对象的时候?/p> <p>为展C此事g的发生情况,假设Z码行 <code>instance =new Singleton();</code> 执行了下列伪代码Q?<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>q段伪代码不仅是可能的,而且是一?JIT ~译器上真实发生的。执行的序是颠倒的Q但鉴于当前的内存模型,q也是允许发生的。JIT ~译器的q一行ؓ使双重检查锁定的问题只不q是一ơ学术实践而已?/p> <p>明这一情况Q假设有清单 5 中的代码。它包含一个剥ȝ?<code>getInstance()</code> Ҏ。我已经删除?#8220;双重查?#8221;以简化我们对生成的汇~代码(清单 6Q的回顾。我们只兛_ JIT ~译器如何编?<code>instance=new Singleton();</code> 代码。此外,我提供了一个简单的构造函数来明确说明汇编代码中该构造函数的q行情况?</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> 为引用下列说明中的汇~代码行Q我引用指令地址的最后两个|因ؓ它们都以 <code>054D20</code> 开头。例如,<code>B5</code> 代表 <code>test eax,eax</code>?/p> <p>汇编代码是通过q行一个在无限循环中调?<code>getInstance()</code> Ҏ的测试程序来生成的。程序运行时Q请q行 Microsoft Visual C++ 调试器ƈ其附到表示试E序?Java q程中。然后,中断执行q找到表C无限循环的汇~代码?/p> <p><code>B0</code> ?<code>B5</code> 处的前两行汇~代码将 <code>instance</code> 引用从内存位|?<code>049388C8</code> 加蝲?<code>eax</code> 中,q进?<code>null</code> 查。这跟清?5 中的 <code>getInstance()</code> Ҏ的第一行代码相对应。第一ơ调用此ҎӞ<code>instance</code> ?<code>null</code>Q代码执行到 <code>B9</code>?code>BE</code> 处的代码?<code>Singleton</code> 对象从堆中分配内存,q将一个指向该块内存的指针存储?<code>eax</code> 中。下一行代码,<code>C3</code>Q获?<code>eax</code> 中的指针q将其存储回内存位置?<code>049388C8</code> 的实例引用。结果是Q?code>instance</code> 现在为非 <code>null</code> q引用一个有效的 <code>Singleton</code> 对象。然而,此对象的构造函数尚未运行,q恰是破坏双重检查锁定的情况。然后,?<code>C8</code> 行处Q?code>instance</code> 指针被解除引用ƈ存储?<code>ecx</code>?code>CA</code> ?<code>D0</code> 行表C内联的构造函敎ͼ该构造函数将?<code>true</code> ?<code>5</code> 存储?<code>Singleton</code> 对象。如果此代码在执?<code>C3</code> 行后且在完成该构造函数前被另一个线E中断,则双重检查锁定就会失败?/p> <p>不是所有的 JIT ~译器都生成如上代码。一些生成了代码Q从而只在构造函数执行后?<code>instance</code> 成ؓ?<code>null</code>。针?Java 技术的 IBM SDK 1.3 版和 Sun JDK 1.3 都生成这L代码。然而,qƈ不意味着应该在这些实例中使用双重查锁定。该习语p|q有一些其他原因。此外,您ƈ不总能知道代码会在哪些 JVM 上运行,?JIT ~译器L会发生变化,从而生成破坏此习语的代码?/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" ><strong>回页?/strong></a></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="4"><span id="wmqeeuq" class="atitle">双重查锁定:获取两个</span></a></p> <p>考虑到当前的双重查锁定不起作用,我加入了另一个版本的代码Q如清单 7 所C,从而防止您刚才看到的无序写入问题?</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 中的代码Q您应该意识C情变得有点荒谬。请CQ创建双重检查锁定是Z避免对简单的三行 <code>getInstance()</code> Ҏ实现同步。清?7 中的代码变得难于控制。另外,该代码没有解决问题。仔l检查可h原因?/p> <p>此代码试N免无序写入问题。它试图通过引入局部变?<code>inst</code> 和第二个 <code>synchronized</code> 块来解决q一问题。该理论实现如下Q?</p> <ol> <li>U程 1 q入 <code>getInstance()</code> Ҏ?br /> <br /> <li>׃ <code>instance</code> ?<code>null</code>Q线E?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>Q线E?1 ?//3 处进入第二个 <code>synchronized</code> 块?<br /> <br /> <li>U程 1 然后开始执?//4 处的代码Q同时 <code>inst</code> 为非 <code>null</code>Q但?<code>Singleton</code> 的构造函数执行前。(q就是我们刚才看到的无序写入问题。) <br /> <br /> <li>U程 1 被线E?2 预占?br /> <br /> <li>U程 2 q入 <code>getInstance()</code> Ҏ?br /> <br /> <li>׃ <code>instance</code> ?<code>null</code>Q线E?2 试图?//1 处进入第一?<code>synchronized</code> 块。由于线E?1 目前持有此锁Q线E?2 被阻断?br /> <br /> <li>U程 1 然后完成 //4 处的执行?br /> <br /> <li>U程 1 然后一个构造完整的 <code>Singleton</code> 对象?//5 处赋值给变量 <code>instance</code>Qƈ退两个 <code>synchronized</code> 块?<br /> <br /> <li>U程 1 q回 <code>instance</code>?br /> <br /> <li>然后执行U程 2 q在 //2 处将 <code>instance</code> 赋值给 <code>inst</code>?br /> <br /> <li>U程 2 发现 <code>instance</code> 为非 <code>null</code>Q将其返回?</li> </ol> <p>q里的关键行?//5。此行应该确?<code>instance</code> 只ؓ <code>null</code> 或引用一个构造完整的 <code>Singleton</code> 对象。该问题发生在理论和实际彼此背道而驰的情况下?/p> <p>׃当前内存模型的定义,清单 7 中的代码无效。Java 语言规范QJava Language SpecificationQJLSQ要求不能将 <code>synchronized</code> 块中的代码移出来。但是,q没有说不能?<code>synchronized</code> 块外面的代码U?em>?/em> <code>synchronized</code> 块中?</p> <p>JIT ~译器会在这里看C个优化的Z。此优化会删?//4 ?//5 处的代码Q组合ƈ且生成清?8 中所C的代码?</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>如果q行此项优化Q您同样遇到我们之前讨的无序写入问题?/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" ><strong>回页?/strong></a></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="5"><span id="wmqeeuq" class="atitle">?volatile 声明每一个变量怎么P</span></a></p> <p>另一个想法是针对变量 <code>inst</code> 以及 <code>instance</code> 使用关键?<code>volatile</code>。根?JLSQ参?<a >参考资?/a>Q,声明?<code>volatile</code> 的变量被认ؓ是顺序一致的Q即Q不是重新排序的。但是试图?<code>volatile</code> 来修正双重检查锁定的问题Q会产生以下两个问题Q?/p> <ul> <li>q里的问题不是有关顺序一致性的Q而是代码被移动了Q不是重新排序?br /> <br /> <li>即考虑了顺序一致性,大多数的 JVM 也没有正地实现 <code>volatile</code>?</li> </ul> <p>W二点值得展开讨论。假设有清单 9 中的代码Q?/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>Ҏ JLSQ由?<code>stop</code> ?<code>num</code> 被声明ؓ <code>volatile</code>Q它们应该顺序一致。这意味着如果 <code>stop</code> 曄?<code>true</code>Q?code>num</code> 一定曾被设|成 <code>100</code>。尽如此,因ؓ许多 JVM 没有实现 <code>volatile</code> 的顺序一致性功能,您就不能依赖此行为。因此,如果U程 1 调用 <code>foo</code> q且U程 2 q发地调?<code>bar</code>Q则U程 1 可能?<code>num</code> 被设|成?<code>100</code> 之前?<code>stop</code> 讄?<code>true</code>。这导致线E见?<code>stop</code> ?<code>true</code>Q?<code>num</code> 仍被讄?<code>0</code>。?<code>volatile</code> ?64 位变量的原子数还有另外一些问题,但这已超Z本文的讨围。有x主题的更多信息,请参?<a >参考资?/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" ><strong>回页?/strong></a></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="6"><span id="wmqeeuq" class="atitle">解决Ҏ</span></a></p> <p>底线是Q无Z何种形式Q都不应使用双重查锁定,因ؓ您不能保证它在Q?JVM 实现上都能顺利运行。JSR-133 是有兛_存模型寻址问题的,管如此Q新的内存模型也不会支持双重查锁定。因此,您有两种选择Q?/p> <ul> <li>接受如清?2 中所C的 <code>getInstance()</code> Ҏ的同步?br /> <br /> <li>攑ּ同步Q而用一?<code>static</code> 字段?</li> </ul> <p>选择?2 如清?10 中所C?/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 的代码没有用同步,q且保调用 <code>static getInstance()</code> Ҏ时才创徏 <code>Singleton</code>。如果您的目标是消除同步Q则q将是一个很好的选择?/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" ><strong>回页?/strong></a></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="7"><span id="wmqeeuq" class="atitle">String 不是不变?/span></a></p> <p>鉴于无序写入和引用在构造函数执行前变成?<code>null</code> 的问题,您可能会考虑 <code>String</code> cR假设有下列代码Q?/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> cd该是不变的。尽如此,鉴于我们之前讨论的无序写入问题,那会在这里导致问题吗Q答案是肯定的。考虑两个U程讉K <code>String str</code>。一个线E能看见 <code>str</code> 引用一?<code>String</code> 对象Q在该对象中构造函数尚未运行。事实上Q清?11 包含展示q种情况发生的代码。注意,q个代码仅在我测试用的旧?JVM 上会p|。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 处创Z?<code>MutableString</code> c,它包含了一?<code>String</code> 引用Q此引用?//3 处的两个U程׃n。在?//5 ?//6 处,在两个分开的线E上创徏了两个对?<code>StringCreator</code> ?<code>StringReader</code>。传入一?<code>MutableString</code> 对象的引用?code>StringCreator</code> c进入到一个无限@环中q且使用?#8220;hello”?//1 处创?<code>String</code> 对象?code>StringReader</code> 也进入到一个无限@环中Qƈ且在 //2 处检查当前的 <code>String</code> 对象的值是不是 “hello”。如果不行,<code>StringReader</code> U程打印Z条消息ƈ停止。如?<code>String</code> cL不变的,则从此程序应当看不到M输出。如果发生了无序写入问题Q则?<code>StringReader</code> 看到 <code>str</code> 引用的惟一Ҏl不是gؓ“hello”?<code>String</code> 对象?/p> <p>在旧版的 JVM ?Sun JDK 1.2.1 上运行此代码会导致无序写入问题。ƈ因此D一个非不变?<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" ><strong>回页?/strong></a></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="7"><span id="wmqeeuq" class="atitle">l束?/span></a></p> <p>为避免单例中代h高昂的同步,E序员非常聪明地发明了双重检查锁定习语。不q的是,鉴于当前的内存模型的原因Q该习语未得到q泛使用Q就明显成ؓ了一U不安全的编E结构。重定义脆弱的内存模型这一领域的工作正在进行中。尽如此,即是在新提议的内存模型中,双重查锁定也是无效的。对此问题最佳的解决Ҏ是接受同步或者用一?<code>static field</code>?</p> </td> </tr> </tbody> </table> <img src ="http://www.aygfsteel.com/Todd/aggbug/318917.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/Todd/" target="_blank">Todd</a> 2010-04-21 00:49 <a href="http://www.aygfsteel.com/Todd/archive/2010/04/21/318917.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【{】在Java与CE序间进行socket通信的讨?/title><link>http://www.aygfsteel.com/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.aygfsteel.com/Todd/archive/2010/04/15/318405.html</guid><wfw:comment>http://www.aygfsteel.com/Todd/comments/318405.html</wfw:comment><comments>http://www.aygfsteel.com/Todd/archive/2010/04/15/318405.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/Todd/comments/commentRss/318405.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/Todd/services/trackbacks/318405.html</trackback:ping><description><![CDATA[<br /> <br /> 1. 背景 <br /> 使用socket在JavaE序与CE序间进行进E间通信。本文主要描qC在同CE序q行通信的Client端的Java实现功能?<br /> <br /> 1.1. 使用的语a <br /> Client端:JavaQJVMQJDK1.3Q?<br /> Server端:CQUNIXQSun SolarisQ?<br /> <br /> 1.2. 讨论范围 <br /> 数据发送:只涉及到Java中int整型pd的讨论,包括byteQshortQint?<br /> 数据接受Q涉及到byteQshortQintQlongQfloatQdoubleQchar?<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发送数据到CE序端?<br /> 输入:使用DataInputStream从CE序端接受数?<br /> <br /> 2.1. 数据发?<br /> ׃DataOutputStream对于Java各个基本数据cd都相应地提供?#8220;?#8221;ҎQ如wrightShort和wrightInt{,因此当进行进E间通信Qsockect通信Q时Q我们L优先考虑使用DataOutputStream?<br /> 下面我们对DataOutputStream及其成员方法进行分析: <br /> <br /> 2.1.1. DataOutputStream?<br /> DataOutputStream实C接口DataOutput?<br /> 本文只讨论writeByte(int v)、writeShort(int v)和writeInt(int v)部分Q这是因为我们需要发送的数据只涉及到intQshort和byteQ其它的longQdouble{则不在q里介绍Q,而且它们都有一个共同的特征Q即唯一的intcd的输入参数?<br /> q些成员Ҏ的功能描qC为我们以后手动进行字节顺序{换,提供了理Z据?<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 /> 规定Q网l上传输的数据统一采用Big Endian格式Q即“高字节在?#8221;Q,我们UC?#8220;|络字节序”Qnetwork byte orderQ?<br /> <br /> Big Endian格式Q?<br /> 高字?低字?<br /> 1 2 3 4 <br /> Byte[0] byte[1] byte[2] byte[3]输出~冲?<br /> <br /> 因此Q无论本机字节顺序采用的那种序Q在发送到|络之前都要转化为网l字节顺序,才能q行传输。特别是在Java与C两种不同语言的应用程序间q行通信Ӟq一点优为重要。(若是两个JavaE序间通信时可能只要保证接受与发送采用相同的字节序Q则可以不进行{换格式,但这U做法ƈ不好Q不h良好的移植性) <br /> <br /> 2.1.3. 数据发送:手动字节转换 / writeIntҎ <br /> 以writeInt(int v)Zq行描述Q?<br /> 阅读DataOutput的writeInt(int v)Ҏ的文档可知: <br /> 使用writeIntҎ可以写一?-byte的int值v到输出流Q其字节序? <br /> <br /> (byte)(0xff & (v >> 24)) byte[0] 高字?<br /> (byte)(0xff & (v >> 16)) byte[1] <br /> (byte)(0xff & (v >> 8)) byte[2] <br /> (byte)(0xff & v) byte[3] 低字?<br /> q样的字节顺序ؓBig Endian格式Q标准的“|络字节序”?<br /> 但是在实际工作中输出采用DataOutputStream.readInt(int)Ҏ时写数据出错Q需要自己手动按照以上所说的寚w要写的vD行{换(通过UM完成Q,转换的代码如下所C,可参见程序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<length; i++,j--) { <br /> if ( j+offset >= 0 && j+offset < 1024 ) { <br /> buffer[j+offset] = (byte)( (value >> i*8) & 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各个基本数据cd都相应地提供?#8220;?#8221;ҎQ如readShort和readInt{,因此当进行进E间通信Qsockect通信Q时Q我们L优先考虑使用DataInputStream?<br /> 而与数据发送不同的是,DataInputStream下的成员Ҏl实际测试,“基本上可?#8221;Ҏ数据cd正确d相应的数倹{?<br /> 但ƈ非完,特别是与不同语言的应用程序进行通信Ӟ如CQ?<br /> <br /> Ҏ?QJava与C的数据类型的比较Q可知: <br /> (1)long型的字节数在Java和C中相?个字节: <br /> 因此由readLongҎL的数值应q行带符L右移32Q?-byteQ位才能得到在CE序中相应的long型数倹{?<br /> Type Java C <br /> long 8-Byte 4-Byte <br /> <br /> (2)׃Java中的char型ؓ2个字节,C中的char型ؓ1个字节,因此不能使用readCharҎ来读取CE序中的char数倹{?<br /> 然而在Java中byte型ؓ1个字节长Q因此可以用readByteҎ得到CE序中的char型数倹{?<br /> Type Java C <br /> byte 1-Byte N/A <br /> char 2-Byte 1-Byte <img src ="http://www.aygfsteel.com/Todd/aggbug/318405.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/Todd/" target="_blank">Todd</a> 2010-04-15 13:29 <a href="http://www.aygfsteel.com/Todd/archive/2010/04/15/318405.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【{】网l字节序与主机字节序http://www.aygfsteel.com/Todd/archive/2010/04/15/318387.htmlToddToddThu, 15 Apr 2010 02:37:00 GMThttp://www.aygfsteel.com/Todd/archive/2010/04/15/318387.htmlhttp://www.aygfsteel.com/Todd/comments/318387.htmlhttp://www.aygfsteel.com/Todd/archive/2010/04/15/318387.html#Feedback0http://www.aygfsteel.com/Todd/comments/commentRss/318387.htmlhttp://www.aygfsteel.com/Todd/services/trackbacks/318387.html最q在目开发过E中Q需要在采用JAVA作ؓ语言的服务器与采用C++作ؓ语言的服务器间进行通信Q这涉及到q两U语a间数据类型的转换以及|络字节序与L字节序的区别。该文主要说说网l字节序和主机字节序的区别以及Little endian与Big endian的概c其实编E的事就比较单了
   我也懒得写了Q直接引用了我觉得写的挺好的两篇文章Q?br />

什么是Big Endian和Little EndianQ?/a>

来源Qhttp://blog.ednchina.com/qinyonglyz/194674/message.aspx

1Q故事的h

“endian”q个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开q是从小?Little-Endian)敲开Q由此曾发生q六ơ叛乱,其中一个皇帝送了命,另一个丢了王位?/p>

我们一般将endian译?#8220;字节?#8221;Q将big endian和little endianUC“大尾”?#8220;尾”?/p>

2Q什么是Big Endian和Little EndianQ?/strong>

在设计计机pȝ的时候,有两U处理内存中数据的方法。一U叫为little-endianQ存攑֜内存中最低位的数值是来自数据的最双部分Q也是数据的最低位部分Q。比如一?6q制数字0x12345678Q在内存存放的方式如下:

?/p>

0111Q?000

0101Q?110

0011Q?100

0001Q?010

地址

100

101

102

103

            另一U称为big-endianQ正好相反,存放在内存中最低位的数值是来自数据的最左边辚w分(也就是数据的最高ؓ部分Q。比如一?6q制数字0x12345678Q在内存存放的方式如下:

?/p>

0001Q?010

0011Q?100

0101Q?110

0111Q?000

地址

100

101

102

103

比如某些文g需要在不同q_处理Q或者通过Socket通信。这斚w我们可以借助ntohl(), ntohs(), htonl(), and htons()函数q行格式转换?/p>

3Q如何判断系l是Big Endianq是Little EndianQ?/strong>

?usr/include/中(包括子目录)查找字符串BYTE_ORDER(或_BYTE_ORDER, __BYTE_ORDER)Q确定其倹{这个g般在endian.h或machine/endian.h文g中可以找?有时在feature.h中,不同?a onclick="javascript:tagshow(event, '%B2%D9%D7%F7%CF%B5%CD%B3');" href="javascript:;" target="_self">操作pȝ可能有所不同。一般来_Little EndianpȝBYTE_ORDER(或_BYTE_ORDER,__BYTE_ORDER)?234QBig Endianpȝ?321。大部分用户的操作系l(如windows, FreeBsd,LinuxQ是Little Endian的。少部分Q如MAC OS ,是Big Endian 的。本质上_Little Endianq是Big Endian与操作系l和芯片cd都有关系?/p>

======================================================================

ext3 文gpȝ在硬盘分Z的数据是按照 Intel ?Little-endian 格式存放的,如果是在 PC 以外的^C开?ext3 相关的程序,要特别注意这一炏V?/font>

谈到字节序的问题Q必然牵涉到两大 CPUz。那是Motorola的PowerPCpdCPU和Intel的x86pdCPU。PowerPCpd采用big endian方式存储数据Q而x86pd则采用little endian方式存储数据。那么究竟什么是big endianQ什么又是little endian呢?

     其实big endian是指低地址存放最高有效字节(MSBQ,而little endian则是低地址存放最低有效字节(LSBQ?/font>

     用文字说明可能比较抽象,下面用图像加以说明。比如数?x12345678在两U不同字节序CPU中的存储序如下所C:

Big Endian

   低地址                                            高地址
   ----------------------------------------->
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     12     |      34    |     56      |     78    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Little Endian

   低地址                                            高地址
   ----------------------------------------->
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     78     |      56    |     34      |     12    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

     从上面两囑֏以看出,采用big endian方式存储数据是符合我们hcȝ思维习惯的。而little endianQ?@#$%^&*Q见鬼去?-_-|||

     Z么要注意字节序的问题呢?你可能这么问。当Ӟ如果你写的程序只在单机环境下面运行,q且不和别h的程序打交道Q那么你完全可以忽略字节序的存在。但是,如果你的E序要跟别h的程序生交互呢Q在q里我想说说两种语言。C/C++语言~写的程序里数据存储序是跟~译q_所在的CPU相关的,?JAVA~写的程序则唯一采用big endian方式来存储数据。试惻I如果你用C/C++语言在x86q_下编写的E序跟别人的JAVAE序互通时会生什么结果?拿上面?0x12345678来说Q你的程序传递给别h的一个数据,指?x12345678的指针传l了JAVAE序Q由于JAVA采取big endian方式存储数据Q很自然的它会将你的数据译?x78563412。什么?竟然变成另外一个数字了Q是的,是q种后果。因此,在你的CE序传给JAVAE序之前有必要进行字节序的{?a onclick="javascript:tagshow(event, '%B9%A4%D7%F7');" href="javascript:;" target="_self">工作?br />
     无独有偶Q所有网l协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式UC为网l字节序。当两台采用不同字节序的L通信Ӟ在发送数据之前都必须l过字节序的转换成ؓ|络字节序后再进行传输?/font>


 

|络字节序与L字节?/a>


来源Qhttp://www.cnblogs.com/jacktu/archive/2008/11/24/1339789.html
不同的CPU有不同的字节序类?q些字节序是指整数在内存中保存的序 q个叫做L?br /> 最常见的有两种
1Q?Little endianQ将低序字节存储在v始地址
2Q?Big endianQ将高序字节存储在v始地址

LE little-endian
最W合人的思维的字节序
地址低位存储值的低位
地址高位存储值的高位
怎么讲是最W合人的思维的字节序Q是因ؓ从h的第一观感来说
低位值小Q就应该攑֜内存地址的地方Q也卛_存地址低位
反之Q高位值就应该攑֜内存地址大的地方Q也卛_存地址高位

BE big-endian
最直观的字节序
地址低位存储值的高位
地址高位存储值的低位
Z么说直观Q不要考虑对应关系
只需要把内存地址从左到右按照׃到高的顺序写?br /> 把值按照通常的高位到低位的顺序写?br /> 两者对照,一个字节一个字节的填充q去

例子Q在内存中双?x01020304(DWORD)的存储方?br />
内存地址
4000 4001 4002 4003
LE 04 03 02 01
BE 01 02 03 04

例子Q如果我们将0x1234abcd写入C0x0000开始的内存中,则结果ؓ
      big-endian  little-endian
0x0000  0x12      0xcd
0x0001  0x23      0xab
0x0002  0xab      0x34
0x0003  0xcd      0x12
x86pdCPU都是little-endian的字节序.

|络字节序是TCP/IP中规定好的一U数据表C格式,它与具体的CPUcd、操作系l等无关Q从而可以保证数据在不同L之间传输时能够被正确解释。网l字节顺序采用big endian排序方式?br />
Zq行转换 bsd socket提供了{换的函数 有下面四?br /> htons 把unsigned shortcd从主机序转换到网l序
htonl 把unsigned longcd从主机序转换到网l序
ntohs 把unsigned shortcd从网l序转换C机序
ntohl 把unsigned longcd从网l序转换C机序

在用little endian的系l中 q些函数会把字节序进行{?br /> 在用big endiancd的系l中 q些函数会定义成I宏

同样 在网l程序开发时 或是跨^台开发时 也应该注意保证只用一U字节序 不然两方的解释不一样就会生bug.

注:
1、网l与L字节转换函数:htons ntohs htonl ntohl (s 是short l是long h是host n是network)
2、不同的CPU上运行不同的操作pȝQ字节序也是不同的,参见下表?br /> 处理?nbsp;   操作pȝ    字节排序
Alpha    全部    Little endian
HP-PA    NT    Little endian
HP-PA    UNIX    Big endian
Intelx86    全部    Little endian <-----x86pȝ是小端字节序pȝ
Motorola680x()    全部    Big endian
MIPS    NT    Little endian
MIPS    UNIX    Big endian
PowerPC    NT    Little endian
PowerPC    非NT    Big endian  <-----PPCpȝ是大端字节序pȝ
RS/6000    UNIX    Big endian
SPARC    UNIX    Big endian
IXP1200 ARM核心    全部    Little endian
=============================================
字节序{换类Q?br />

/**
* 通信格式转换
*
* Java和一些windows~程语言如c、c++、delphi所写的|络E序q行通讯Ӟ需要进行相应的转换
* 高、低字节之间的{?br /> * windows的字节序Z字节开?br /> * linux,unix的字节序为高字节开?br /> * java则无论^台变化,都是高字节开?br /> */

public class FormatTransfer {
/**
  * int转ؓ低字节在前,高字节在后的byte数组
  * @param n int
  * @return byte[]
  */
public static byte[] toLH(int n) {
  byte[] b = new byte[4];
  b[0] = (byte) (n & 0xff);
  b[1] = (byte) (n >> 8 & 0xff);
  b[2] = (byte) (n >> 16 & 0xff);
  b[3] = (byte) (n >> 24 & 0xff);
  return b;
}

/**
  * int转ؓ高字节在前,低字节在后的byte数组
  * @param n int
  * @return byte[]
  */
public static byte[] toHH(int n) {
  byte[] b = new byte[4];
  b[3] = (byte) (n & 0xff);
  b[2] = (byte) (n >> 8 & 0xff);
  b[1] = (byte) (n >> 16 & 0xff);
  b[0] = (byte) (n >> 24 & 0xff);
  return b;
}

/**
  * short转ؓ低字节在前,高字节在后的byte数组
  * @param n short
  * @return byte[]
  */
public static byte[] toLH(short n) {
  byte[] b = new byte[2];
  b[0] = (byte) (n & 0xff);
  b[1] = (byte) (n >> 8 & 0xff);
  return b;
}

/**
  * short转ؓ高字节在前,低字节在后的byte数组
  * @param n short
  * @return byte[]
  */
public static byte[] toHH(short n) {
  byte[] b = new byte[2];
  b[1] = (byte) (n & 0xff);
  b[0] = (byte) (n >> 8 & 0xff);
  return b;
}

 

/**
  * 将int转ؓ高字节在前,低字节在后的byte数组

public static byte[] toHH(int number) {
  int temp = number;
  byte[] b = new byte[4];
  for (int i = b.length - 1; i > -1; i--) {
    b = new Integer(temp & 0xff).byteValue();
    temp = temp >> 8;
  }
  return b;
}

public static byte[] IntToByteArray(int i) {
    byte[] abyte0 = new byte[4];
    abyte0[3] = (byte) (0xff & i);
    abyte0[2] = (byte) ((0xff00 & i) >> 8);
    abyte0[1] = (byte) ((0xff0000 & i) >> 16);
    abyte0[0] = (byte) ((0xff000000 & i) >> 24);
    return abyte0;
}


*/

/**
  * float转ؓ低字节在前,高字节在后的byte数组
  */
public static byte[] toLH(float f) {
  return toLH(Float.floatToRawIntBits(f));
}

/**
  * float转ؓ高字节在前,低字节在后的byte数组
  */
public static byte[] toHH(float f) {
  return toHH(Float.floatToRawIntBits(f));
}

/**
  * String转ؓbyte数组
  */
public static byte[] stringToBytes(String s, int length) {
  while (s.getBytes().length < length) {
    s += " ";
  }
  return s.getBytes();
}


/**
  * 字节数l{换ؓString
  * @param b byte[]
  * @return String
  */
public static String bytesToString(byte[] b) {
  StringBuffer result = new StringBuffer("");
  int length = b.length;
  for (int i=0; i<length; i++) {
    result.append((char)(b & 0xff));
  }
  return result.toString();
}

/**
  * 字W串转换为byte数组
  * @param s String
  * @return byte[]
  */
public static byte[] stringToBytes(String s) {
  return s.getBytes();
}

/**
  * 高字节数组转换为int
  * @param b byte[]
  * @return int
  */
public static int hBytesToInt(byte[] b) {
  int s = 0;
  for (int i = 0; i < 3; i++) {
    if (b >= 0) {
    s = s + b;
    } else {
    s = s + 256 + b;
    }
    s = s * 256;
  }
  if (b[3] >= 0) {
    s = s + b[3];
  } else {
    s = s + 256 + b[3];
  }
  return s;
}

/**
  * 低字节数组转换为int
  * @param b byte[]
  * @return int
  */
public static int lBytesToInt(byte[] b) {
  int s = 0;
  for (int i = 0; i < 3; i++) {
    if (b[3-i] >= 0) {
    s = s + b[3-i];
    } else {
    s = s + 256 + b[3-i];
    }
    s = s * 256;
  }
  if (b[0] >= 0) {
    s = s + b[0];
  } else {
    s = s + 256 + b[0];
  }
  return s;
}


/**
  * 高字节数l到short的{?br />   * @param b byte[]
  * @return short
  */
public static short hBytesToShort(byte[] b) {
  int s = 0;
  if (b[0] >= 0) {
    s = s + b[0];
    } else {
    s = s + 256 + b[0];
    }
    s = s * 256;
  if (b[1] >= 0) {
    s = s + b[1];
  } else {
    s = s + 256 + b[1];
  }
  short result = (short)s;
  return result;
}

/**
  * 低字节数l到short的{?br />   * @param b byte[]
  * @return short
  */
public static short lBytesToShort(byte[] b) {
  int s = 0;
  if (b[1] >= 0) {
    s = s + b[1];
    } else {
    s = s + 256 + b[1];
    }
    s = s * 256;
  if (b[0] >= 0) {
    s = s + b[0];
  } else {
    s = s + 256 + b[0];
  }
  short result = (short)s;
  return result;
}

/**
  * 高字节数l{换ؓfloat
  * @param b byte[]
  * @return float
  */
public static float hBytesToFloat(byte[] b) {
  int i = 0;
  Float F = new Float(0.0);
  i = ((((b[0]&0xff)<<8 | (b[1]&0xff))<<8) | (b[2]&0xff))<<8 | (b[3]&0xff);
  return F.intBitsToFloat(i);
}

/**
  * 低字节数l{换ؓfloat
  * @param b byte[]
  * @return float
  */
public static float lBytesToFloat(byte[] b) {
  int i = 0;
  Float F = new Float(0.0);
  i = ((((b[3]&0xff)<<8 | (b[2]&0xff))<<8) | (b[1]&0xff))<<8 | (b[0]&0xff);
  return F.intBitsToFloat(i);
}

/**
  * byte数组中的元素倒序排列
  */
public static byte[] bytesReverseOrder(byte[] b) {
  int length = b.length;
  byte[] result = new byte[length];
  for(int i=0; i<length; i++) {
    result[length-i-1] = b;
  }
  return result;
}

/**
  * 打印byte数组
  */
public static void printBytes(byte[] bb) {
  int length = bb.length;
  for (int i=0; i<length; i++) {
    System.out.print(bb + " ");
  }
  System.out.println("");
}

public static void logBytes(byte[] bb) {
  int length = bb.length;
  String ut = "";
  for (int i=0; i<length; i++) {
    ut = out + bb + " ";
  }

}

/**
  * intcd的D{换ؓ字节序颠倒过来对应的int?br />   * @param i int
  * @return int
  */
public static int reverseInt(int i) {
  int result = FormatTransfer.hBytesToInt(FormatTransfer.toLH(i));
  return result;
}

/**
  * shortcd的D{换ؓ字节序颠倒过来对应的short?br />   * @param s short
  * @return short
  */
public static short reverseShort(short s) {
  short result = FormatTransfer.hBytesToShort(FormatTransfer.toLH(s));
  return result;
}

/**
  * floatcd的D{换ؓ字节序颠倒过来对应的float?br />   * @param f float
  * @return float
  */
public static float reverseFloat(float f) {
  float result = FormatTransfer.hBytesToFloat(FormatTransfer.toLH(f));
  return result;
}

}



Todd 2010-04-15 10:37 发表评论
]]>
ConcurrentHashMap应用注意事项http://www.aygfsteel.com/Todd/archive/2010/04/13/318111.htmlToddToddTue, 13 Apr 2010 01:23:00 GMThttp://www.aygfsteel.com/Todd/archive/2010/04/13/318111.htmlhttp://www.aygfsteel.com/Todd/comments/318111.htmlhttp://www.aygfsteel.com/Todd/archive/2010/04/13/318111.html#Feedback0http://www.aygfsteel.com/Todd/comments/commentRss/318111.htmlhttp://www.aygfsteel.com/Todd/services/trackbacks/318111.html 2、keySet().iterator()及keys()Q获取的Iterator、Enumeration变量是单U程讉K安全的,多线E访问时要么生成多个Iterator、Enumeration(通过调用相应的获取方?Q要么以ConcurrentHashMap变量为锁q行同步(synchronized该变?QConcurrentHashMap变量是多U程讉K安全的,管是多U程讉KQ多数情况下应该没有锁争用;
3、put、removeҎ要用锁Q但q不一定有锁争用,原因在于ConcurrentHashMap缓存的变量分到多个SegmentQ每个Segment上有一个锁Q只要多个线E访问的不是一个Segment没有锁争用Q就没有堵塞Q各U程用各自的锁,ConcurrentHashMap~省情况下生?6个SegmentQ也是允许16个线Eƈ发的更新而尽量没有锁争用Q?br /> 4、Iterator、Enumeration获得的对象,不一定是和其它更新线E同步,获得的对象可能是更新前的对象QConcurrentHashMap允许一Ҏ新、一辚w历,未遍历到的key一般能放映value更新Q?br /> 5、有些情况下q种不一致是允许的,如果需要最大的性能、吞吐量Q则正好使用ConcurrentHashMap?

目前只想到能用于~存无关紧要的信息,对于d 都须同步的操作,竟然q要加synchronizedQ悲剧的U程安全

Todd 2010-04-13 09:23 发表评论
]]>
վ֩ģ壺 | ɽ| | | | | | | | | Ͽ| ӽ| ɽʡ| | | | Ϣ| Թ| | | | ȫ| ױ| | | ϻ| | | | | | | | Ͻ| | | | ƽ| | ڽ| פ|