﻿<?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-当Endisoft爱上了JAVA</title><link>http://www.blogjava.net/endisoft/</link><description>I believe I can fly,I can touch the sky</description><language>zh-cn</language><lastBuildDate>Tue, 28 Apr 2026 21:26:40 GMT</lastBuildDate><pubDate>Tue, 28 Apr 2026 21:26:40 GMT</pubDate><ttl>60</ttl><item><title>做什么技术工资最高？</title><link>http://www.blogjava.net/endisoft/archive/2006/09/29/72715.html</link><dc:creator>Endisoft</dc:creator><author>Endisoft</author><pubDate>Fri, 29 Sep 2006 01:01:00 GMT</pubDate><guid>http://www.blogjava.net/endisoft/archive/2006/09/29/72715.html</guid><wfw:comment>http://www.blogjava.net/endisoft/comments/72715.html</wfw:comment><comments>http://www.blogjava.net/endisoft/archive/2006/09/29/72715.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/endisoft/comments/commentRss/72715.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/endisoft/services/trackbacks/72715.html</trackback:ping><description><![CDATA[
		<div>看看indeed.com的数据：</div>
		<div> </div>
		<div style="MARGIN-LEFT: 39pt; TEXT-INDENT: -21pt">
				<span>1．<strong> </strong></span>
				<strong>做什么语言收入最高？</strong>
		</div>
		<div style="MARGIN-LEFT: 0.25in">
				<a href="http://www.indeed.com/salary?q1=lisp&amp;l1=&amp;q2=c%2B%2B&amp;l2=&amp;q3=java&amp;l3=&amp;q4=c%23&amp;l4=&amp;q5=python&amp;l5=&amp;q6=ruby&amp;l6=&amp;q7=smalltalk&amp;l7=&amp;q8=cobol&amp;l8=&amp;q9=Perl&amp;l9=&amp;q10=Erlang&amp;l10=&amp;q11=Prolog&amp;l11=&amp;q12=C&amp;l12=&amp;q13=Ada&amp;l13=&amp;tm=1">http://www.indeed.com/salary?q1=lisp&amp;l1=&amp;q2=c%2B%2B&amp;l2=&amp;q3=java&amp;l3=&amp;q4=c%23&amp;l4=&amp;q5=python&amp;l5=&amp;q6=ruby&amp;l6=&amp;q7=smalltalk&amp;l7=&amp;q8=cobol&amp;l8=&amp;q9=Perl&amp;l9=&amp;q10=Erlang&amp;l10=&amp;q11=Prolog&amp;l11=&amp;q12=C&amp;l12=&amp;q13=Ada&amp;l13=&amp;tm=1</a>
		</div>
		<div style="MARGIN-LEFT: 0.25in">
				<img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/g9yuayon/c7e132ce210a408996fb8dc6e7a3471b.png" />
		</div>
		<div style="MARGIN-LEFT: 0.25in"> </div>
		<div style="MARGIN-LEFT: 0.25in">每次最多比较13种语言。于是我把没有数据的Erlang换成OCaml，军方才用的Ada换成Haskell，和不知道中国有谁用的Prolog换成Assembly，把C换成Visual Basic, 把Cobol换成Delphi:</div>
		<div style="MARGIN-LEFT: 0.25in">
				<a href="http://www.indeed.com/salary?q1=lisp&amp;l1=&amp;q2=c%2B%2B&amp;l2=&amp;q3=java&amp;l3=&amp;q4=c%23&amp;l4=&amp;q5=python&amp;l5=&amp;q6=ruby&amp;l6=&amp;q7=smalltalk&amp;l7=&amp;q8=Delphi&amp;l8=&amp;q9=Perl&amp;l9=&amp;q10=OCaml&amp;l10=&amp;q11=Assembly&amp;l11=&amp;q12=Visual+Basic&amp;l12=&amp;q13=Haskell&amp;l13=&amp;tm=1">http://www.indeed.com/salary?q1=lisp&amp;l1=&amp;q2=c%2B%2B&amp;l2=&amp;q3=java&amp;l3=&amp;q4=c%23&amp;l4=&amp;q5=python&amp;l5=&amp;q6=ruby&amp;l6=&amp;q7=smalltalk&amp;l7=&amp;q8=Delphi&amp;l8=&amp;q9=Perl&amp;l9=&amp;q10=OCaml&amp;l10=&amp;q11=Assembly&amp;l11=&amp;q12=Visual+Basic&amp;l12=&amp;q13=Haskell&amp;l13=&amp;tm=1</a>
		</div>
		<div style="MARGIN-LEFT: 0.25in"> </div>
		<div style="MARGIN-LEFT: 0.25in">
				<img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/g9yuayon/3c41bb75bf7440a3aa15b1869151c23f.png" />
		</div>
		<div style="MARGIN-LEFT: 0.25in">OCaml和Haskell很惨哈。</div>
		<div style="MARGIN-LEFT: 0.25in"> </div>
		<div style="MARGIN-LEFT: 0.25in">
				<strong>Web开发呢</strong>？<a href="http://www.indeed.com/salary?q1=php&amp;l1=&amp;q2=ruby+on+rails&amp;l2=&amp;q3=javascript&amp;l3=&amp;q4=xhtml&amp;l4=&amp;q5=flex&amp;l5=&amp;q6=openlaslo&amp;l6=&amp;q7=flash&amp;l7=&amp;q8=coldfusion&amp;l8=&amp;q9=jsp&amp;l9=&amp;q10=asp&amp;l10=&amp;q11=asp.net&amp;l11=&amp;q12=vbscript&amp;l12=&amp;tm=1">http://www.indeed.com/salary?q1=php&amp;l1=&amp;q2=ruby+on+rails&amp;l2=&amp;q3=javascript&amp;l3=&amp;q4=xhtml&amp;l4=&amp;q5=flex&amp;l5=&amp;q6=openlaslo&amp;l6=&amp;q7=flash&amp;l7=&amp;q8=coldfusion&amp;l8=&amp;q9=jsp&amp;l9=&amp;q10=asp&amp;l10=&amp;q11=asp.net&amp;l11=&amp;q12=vbscript&amp;l12=&amp;tm=1</a></div>
		<div style="MARGIN-LEFT: 0.25in"> </div>
		<div style="MARGIN-LEFT: 0.25in">
				<img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/g9yuayon/aa666bece6054ff2bdffc8631afc903a.png" />
		</div>
		<div style="MARGIN-LEFT: 0.25in"> </div>
		<div style="MARGIN-LEFT: 39pt; TEXT-INDENT: -21pt">
				<span>2． </span>
				<strong>做什么系统收入高</strong>：<br /><a href="http://www.indeed.com/salary?q1=vxworks&amp;l1=&amp;q2=linux&amp;l2=&amp;q3=windows&amp;l3=&amp;q4=Solaris&amp;l4=&amp;q5=BSD&amp;l5=&amp;tm=1">http://www.indeed.com/salary?q1=vxworks&amp;l1=&amp;q2=linux&amp;l2=&amp;q3=windows&amp;l3=&amp;q4=Solaris&amp;l4=&amp;q5=BSD&amp;l5=&amp;tm=1</a><br /><br /></div>
		<div style="MARGIN-LEFT: 0.25in">  
<div style="MARGIN-LEFT: 0.25in"><img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/g9yuayon/09efc6b7d3f0404abc88cc4cf0f66b7d.png" /></div><div style="MARGIN-LEFT: 0.25in">做RTOS的老大们可以笑了。</div></div>
		<div style="MARGIN-LEFT: 0.25in"> </div>
		<div style="MARGIN-LEFT: 39pt; TEXT-INDENT: -21pt">
				<span>3． </span>
				<strong>做什么技术收入高</strong>
		</div>
		<div style="MARGIN-LEFT: 0.25in">
				<a href="http://www.indeed.com/salary?q1=data+mining&amp;l1=&amp;q2=web+services&amp;l2=&amp;q3=information+retrieval&amp;l3=&amp;q4=distributed+computing&amp;l4=&amp;q5=compiler&amp;l5=&amp;q6=operating+system&amp;l6=&amp;q7=artificial+intelligence&amp;l7=&amp;q8=parallel+computing&amp;l8=&amp;q9=CPU&amp;l9=&amp;q10=grid+computing&amp;l10=&amp;q11=MIS&amp;l11=&amp;q12=3D+engine&amp;l12=&amp;q13=SOA&amp;l13=&amp;tm=1">http://www.indeed.com/salary?q1=data+mining&amp;l1=&amp;q2=web+services&amp;l2=&amp;q3=information+retrieval&amp;l3=&amp;q4=distributed+computing&amp;l4=&amp;q5=compiler&amp;l5=&amp;q6=operating+system&amp;l6=&amp;q7=artificial+intelligence&amp;l7=&amp;q8=parallel+computing&amp;l8=&amp;q9=CPU&amp;l9=&amp;q10=grid+computing&amp;l10=&amp;q11=MIS&amp;l11=&amp;q12=3D+engine&amp;l12=&amp;q13=SOA&amp;l13=&amp;tm=1</a>
		</div>
		<div style="MARGIN-LEFT: 0.25in">
				<img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/g9yuayon/b54e48c6464c402ca79a52872be5b00f.png" />
		</div>
		<div style="MARGIN-LEFT: 0.25in">比较搞笑的是grid computing才20000， 低于贫困线啊。搞并行计算的灰常牛哈。这个跟若干大公司开出6位数工资请能搞定<a href="http://www-304.ibm.com/jct03004c/businesscenter/venturedevelopment/us/en/featurearticle/gcl_xmlid/8649/nav_id/emerging">CELL</a>架构（PlayStation3用的处理器就是Cell）下的编译器的高手一致哈。</div>
		<div style="MARGIN-LEFT: 0.25in"> </div>
		<div style="MARGIN-LEFT: 0.25in">学过统计的老大们不要怒。俺知道，俺知道，中值，均值，样本空间，标准偏差，统计分布，曲线不对称程度（skew），时间片，数据重合处理，噪音排除。。。等等都没有。数据没有说服力。玩儿玩儿而已，博君一笑。</div>
		<p> 再加几个具体技术，比如SAS和Matlab。小问题：为什么matlab那么牛B嗫？</p>
		<div>
				<img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/g9yuayon/ee3836b1d9f145eba42ffee969068a1a.png" />
		</div>
		<div>个人以为：很多要matlab的职位是华尔街上的quants一类的工作。也就是说，不在于你matlab玩儿得多转，而在于你数学多牛。好比用相机，不在于你自己相机的说明书多熟，而在于你对摄影艺术的把握。</div>
		<br />
<img src ="http://www.blogjava.net/endisoft/aggbug/72715.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/endisoft/" target="_blank">Endisoft</a> 2006-09-29 09:01 <a href="http://www.blogjava.net/endisoft/archive/2006/09/29/72715.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>单态设计模式</title><link>http://www.blogjava.net/endisoft/archive/2006/09/27/72178.html</link><dc:creator>Endisoft</dc:creator><author>Endisoft</author><pubDate>Wed, 27 Sep 2006 01:11:00 GMT</pubDate><guid>http://www.blogjava.net/endisoft/archive/2006/09/27/72178.html</guid><wfw:comment>http://www.blogjava.net/endisoft/comments/72178.html</wfw:comment><comments>http://www.blogjava.net/endisoft/archive/2006/09/27/72178.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/endisoft/comments/commentRss/72178.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/endisoft/services/trackbacks/72178.html</trackback:ping><description><![CDATA[
		<font size="2">设计模式之Singleton(单态) </font>
		<p>
				<font size="2">
						<strong>单态定义</strong>:<br />Singleton模式主要作用是保证在Java应用程序中，一个类Class只有一个实例存在。 </font>
		</p>
		<p>
				<font size="2">在很多操作中，比如建立目录 数据库连接都需要这样的单线程操作。</font>
		</p>
		<p>
				<font size="2">还有, singleton能够被状态化; 这样，多个单态类在一起就可以作为一个状态仓库一样向外提供服务，比如，你要论坛中的帖子计数器，每次浏览一次需要计数，单态类能否保持住这个计数，并且能synchronize的安全自动加1，如果你要把这个数字永久保存到数据库，你可以在不修改单态接口的情况下方便的做到。</font>
		</p>
		<p>
				<font size="2">另外方面，Singleton也能够被无状态化。提供工具性质的功能，<br /><br />Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存，因为它限制了实例的个数，有利于Java垃圾回收（garbage collection）。<br /><br />我们常常看到工厂模式中类装入器(class loader)中也用Singleton模式实现的,因为被装入的类实际也属于资源。<br /></font>
		</p>
		<p>
				<font size="2">
						<strong>如何使用?</strong>
						<br />一般Singleton模式通常有几种形式:</font>
		</p>
		<table cellspacing="3" cellpadding="3" width="100%" border="0">
				<tbody>
						<tr>
								<td bgcolor="#cccccc">
										<p>
												<font size="2">public class Singleton {</font>
										</p>
										<p>
												<font size="2">　　private Singleton(){}</font>
										</p>
										<p>
												<font size="2">　　//在自己内部定义自己一个实例，是不是很奇怪？<br />　　//注意这是private 只供内部调用</font>
										</p>
										<p>
												<font size="2">　　private static Singleton instance = new Singleton();</font>
										</p>
										<p>
												<font size="2">　　//这里提供了一个供外部访问本class的静态方法，可以直接访问　　<br />　　public static Singleton getInstance() {<br />　　　　return instance; 　　<br />　　 } <br />}  </font>
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">第二种形式:</font>
		</p>
		<table cellspacing="3" cellpadding="3" width="100%" border="0">
				<tbody>
						<tr>
								<td bgcolor="#cccccc">
										<font size="2">public class Singleton { </font>
										<p>
												<font size="2">　　private static Singleton instance = null;<br /><br />　　public static synchronized Singleton getInstance() {<br /><br />　　//这个方法比上面有所改进，不用每次都进行生成对象，只是第一次　　　 　<br />　　//使用时生成实例，提高了效率！<br />　　if (instance==null)<br />　　　　instance＝new Singleton();<br />　　return instance; 　　} </font>
										</p>
										<p>
												<font size="2">}  </font>
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">使用Singleton.getInstance()可以访问单态类。</font>
		</p>
		<p>
				<font size="2">上面第二中形式是lazy initialization，也就是说第一次调用时初始Singleton，以后就不用再生成了。</font>
		</p>
		<p>
				<font size="2">注意到lazy initialization形式中的synchronized，这个synchronized很重要，如果没有synchronized，那么使用getInstance()是有可能得到多个Singleton实例。关于lazy initialization的Singleton有很多涉及double-checked locking (DCL)的讨论，有兴趣者进一步研究。</font>
		</p>
		<p>
				<font size="2">一般认为第一种形式要更加安全些。<br /></font>
		</p>
		<p>
				<font size="2">
						<strong>使用Singleton注意事项</strong>：<br />有时在某些情况下，使用Singleton并不能达到Singleton的目的，如有多个Singleton对象同时被不同的类装入器装载；在EJB这样的分布式系统中使用也要注意这种情况，因为EJB是跨服务器，跨JVM的。</font>
		</p>
		<p>
				<font size="2">我们以SUN公司的宠物店源码(Pet Store 1.3.1)的ServiceLocator为例稍微分析一下：<br /><br />在Pet Store中ServiceLocator有两种，一个是EJB目录下；一个是WEB目录下，我们检查这两个ServiceLocator会发现内容差不多，都是提供EJB的查询定位服务，可是为什么要分开呢？仔细研究对这两种ServiceLocator才发现区别：在WEB中的ServiceLocator的采取Singleton模式，ServiceLocator属于资源定位，理所当然应该使用Singleton模式。但是在EJB中，Singleton模式已经失去作用，所以ServiceLocator才分成两种，一种面向WEB服务的，一种是面向EJB服务的。</font>
		</p>
		<p>
				<font size="2">Singleton模式看起来简单，使用方法也很方便，但是真正用好，是非常不容易，需要对Java的类 线程 内存等概念有相当的了解。</font>
		</p>
		<p>
				<font size="2">总之：如果你的应用基于容器，那么Singleton模式少用或者不用，可以使用相关替代技术。</font>
		</p>
<img src ="http://www.blogjava.net/endisoft/aggbug/72178.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/endisoft/" target="_blank">Endisoft</a> 2006-09-27 09:11 <a href="http://www.blogjava.net/endisoft/archive/2006/09/27/72178.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是Spring,Spring能干吗？</title><link>http://www.blogjava.net/endisoft/archive/2006/09/26/72140.html</link><dc:creator>Endisoft</dc:creator><author>Endisoft</author><pubDate>Tue, 26 Sep 2006 15:31:00 GMT</pubDate><guid>http://www.blogjava.net/endisoft/archive/2006/09/26/72140.html</guid><wfw:comment>http://www.blogjava.net/endisoft/comments/72140.html</wfw:comment><comments>http://www.blogjava.net/endisoft/archive/2006/09/26/72140.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/endisoft/comments/commentRss/72140.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/endisoft/services/trackbacks/72140.html</trackback:ping><description><![CDATA[有人说spring aop+ spring ioc, 才~~~是spring<br />简单一点,是一个容器. 什么容器,容纳什么?是对象,或者说bean的容器.<br />那为什么叫轻量级容器呢?相对于EJB container,使用spring不需要写符合容器规范的代码,即容器不会"侵入"了你的代码.<br /><br />这个容器会提供你的应用(程序)中用到的所有对象,并对这些对象进行统一的生命周期管理和组装.在通常的开发中,我们在需要某个对象的时候只是 new MyObject(). 在Java中,这样没有什么不好,因为gc会打理好"善后"工作,是系统级的. 而用spring,在需要某个对象时,只要向容器请求相应的对象,spring会找到并准备好这些对象并提供给你.她也会打理好"善后"工作,但是是在应用级的.<br />另一方面,spring还会帮助你打理对象之间的依赖关系.<br />比如原来的做法:<br /><br />class A{<br />}<br />class B{<br />  A a ;<br />  public B(){ a = new A();}<br />}<br /><br />而使用spring的做法<br />class A{<br />}<br />class B{<br />  A a;<br />  public B(){}<br />  void setA(A a){this.a=a}<br />  A getA(){return this.a}<br />}<br />(希望你不要单纯地认为spring会写很多代码)<br />但从前一个方面,你可能觉得spring只是一个对象容器.从这里你就应该看出,spring是bean容器,因为spring需要你的类符合bean规范:相应于每一个成员域,都需要提供setter和getter方法.spring要使用这些方法来注入依赖关系,也就是 dependence injection, 或者inversion of control. 我个人觉得还是di更容易理解,直到现在我还是要考虑怎么去向别人很好的解释ioc.控制反转(倒转),我的理解是就如同上面的两个例子里看到的,依赖(控制)不在体现在代码逻辑里(如第一个例子),而是在配置文件里,而在代码中我们只提供注入点(也就是setter和getter).<br /><br />希望我对IoC的概念的讲解能够给你一些启发.<br />你可能要问了,为什么我要这样做呢?原来的做法有什么不妥的地方么?没有什么不妥,只是两种理念而已,没有绝对的好还是不好,但我还是给你我的解释--我理解的IoC的好处,希望有所帮助.通常在程序设计的时候,我们在需要某些功能时,会相应的去设计一些方法,然后根据OO去将方法和一成员变量组成一个类.实际上,我们最终设计出的程序是:一组类的实例互相交互完成某个特定的任务.<br />除了一些核心的业务方法,以外我们还要做组装对象的工作.比如我有了一个工厂,里面有很多机器,机器在开动时要装配相应的模具.那么在工厂的生产过程中, 首先我要有工厂,机器,模具这样三个类.然后我的"动作"有:装配,开机.通常的做法我们要做装配,然后再去开机.而用spring,我们只是专注于开机.这样我们就把装配这个动作抽离出了核心的"生产过程".当某些机器改变了装配模具时,不在需要修改核心业务代码.这就是解耦.如:<br /><br />public class Production{<br />  public static void main(String[] args){<br />    Factory factory = (Factory)BeanFactory.getBean("factory");<br />    factory.launchProduction();<br />  }<br />}<br /><br />class Factory{<br />  Machine machine1,machine2;<br />  void launchProduction(){<br />     machine1.start(); machine2.start();<br />  }<br />  // setters and getters<br />}<br /><br />class Machine{<br />  Tool tool;<br />  void start(){<br />  }<br />  // setters and getters<br />}<br /><br />在launchProduction()方法中只需要开动每台机器即可.而不需要每次都装配机器.装配的工作交给了别人.现在只要按下start按钮.生产就开始了!要是原来:<br /><br />void launchProduction(){<br />  machine1 = new MachineA();<br />  machine1.setTool(new ToolA());<br />  machine2 = new MachineB();<br />  machine2.setTool(new ToolB());<br />  machine1.start();<br />  machine2.start();<br />}<br /><br />这就是工作分工,是不是感觉轻松了许多?从此以后,我们都是面向构件去开发,而不需要过多地在代码中体现构件之间的依赖关系.<br /><br />AOP<br />推荐你看一下&lt;&lt;effective enterprise java&gt;&gt;的第一章,对AOP有很清晰,易懂的解释.其实AOP并非很艰深晦涩的概念,但是从架构角度去理解她的重要性可能不是我这样的new fish一时半会儿可以领悟到的.<br />我这里只是想说,有些概念你要知道是怎么回事,但理解到多深,除了天赋以外更多的是经验和悟.所以不要心急.--像是在自我解嘲.<br />也许在不知不觉中你就使用了很多AOP的概念,比如servlet里的filter,比如在写一个command类时,给她的调用类在每次调用command时前后加上:preProcess和postProcess...<br />我不想解释太多,&lt;&lt;eej&gt;&gt;的解释已经足够.<br /><img src ="http://www.blogjava.net/endisoft/aggbug/72140.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/endisoft/" target="_blank">Endisoft</a> 2006-09-26 23:31 <a href="http://www.blogjava.net/endisoft/archive/2006/09/26/72140.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java多线程程序设计详细解析</title><link>http://www.blogjava.net/endisoft/archive/2006/09/21/71209.html</link><dc:creator>Endisoft</dc:creator><author>Endisoft</author><pubDate>Thu, 21 Sep 2006 14:46:00 GMT</pubDate><guid>http://www.blogjava.net/endisoft/archive/2006/09/21/71209.html</guid><wfw:comment>http://www.blogjava.net/endisoft/comments/71209.html</wfw:comment><comments>http://www.blogjava.net/endisoft/archive/2006/09/21/71209.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/endisoft/comments/commentRss/71209.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/endisoft/services/trackbacks/71209.html</trackback:ping><description><![CDATA[
		<p>一、理解多线程</p>
		<p>　　多线程是这样一种机制，它允许在程序中并发执行多个指令流，每个指令流都称为一个线程，彼此间互相独立。</p>
		<p>　　线程又称为轻量级进程，它和进程一样拥有独立的执行控制，由操作系统负责调度，区别在于线程没有独立的存储空间，而是和所属进程中的其它线程共享一个存储空间，这使得线程间的通信远较进程简单。</p>
		<p>　　多个线程的执行是并发的，也就是在逻辑上“同时”，而不管是否是物理上的“同时”。如果系统只有一个CPU，那么真正的“同时”是不可能的，但是由于CPU的速度非常快，用户感觉不到其中的区别，因此我们也不用关心它，只需要设想各个线程是同时执行即可。</p>
		<p>　　多线程和传统的单线程在程序设计上最大的区别在于，由于各个线程的控制流彼此独立，使得各个线程之间的代码是乱序执行的，由此带来的线程调度，同步等问题，将在以后探讨。</p>
		<p>　　二、在Java中实现多线程</p>
		<p>　　我们不妨设想，为了创建一个新的线程，我们需要做些什么？很显然，我们必须指明这个线程所要执行的代码，而这就是在Java中实现多线程我们所需要做的一切！</p>
		<p>　　真是神奇！Java是如何做到这一点的？通过类！作为一个完全面向对象的语言，Java提供了类java.lang.Thread来方便多线程编程，这个类提供了大量的方法来方便我们控制自己的各个线程，我们以后的讨论都将围绕这个类进行。</p>
		<p>　　那么如何提供给 Java 我们要线程执行的代码呢？让我们来看一看 Thread 类。Thread 类最重要的方法是run()，它为Thread类的方法start()所调用，提供我们的线程所要执行的代码。为了指定我们自己的代码，只需要覆盖它！</p>
		<p>　　方法一：继承 Thread 类，覆盖方法 run()，我们在创建的 Thread 类的子类中重写 run() ,加入线程所要执行的代码即可。下面是一个例子：</p>
		<p>　　public class MyThread extends Thread<br />　　{<br />　　int count= 1, number;<br />　　public MyThread(int num)<br />　　{<br />　　number = num;<br />　　System.out.println<br />　　("创建线程 " + number);<br />　　}<br />　　public void run() {<br />　　while(true) {<br />　　System.out.println<br />　　("线程 " + number + ":计数 " + count);<br />　　if(++count== 6) return;<br />　　}<br />　　}<br />　　public static void main(String args[])<br />　　{<br />　　for(int i = 0;<br />　　i 〈 5; i++) new MyThread(i+1).start();<br />　　}<br />　　}<br />　　<br />　　这种方法简单明了，符合大家的习惯，但是，它也有一个很大的缺点，那就是如果我们的类已经从一个类继承（如小程序必须继承自 Applet 类），则无法再继承 Thread 类，这时如果我们又不想建立一个新的类，应该怎么办呢？</p>
		<p>　　我们不妨来探索一种新的方法：我们不创建Thread类的子类，而是直接使用它，那么我们只能将我们的方法作为参数传递给 Thread 类的实例，有点类似回调函数。但是 Java 没有指针，我们只能传递一个包含这个方法的类的实例。</p>
		<p>　　那么如何限制这个类必须包含这一方法呢？当然是使用接口！（虽然抽象类也可满足，但是需要继承，而我们之所以要采用这种新方法，不就是为了避免继承带来的限制吗？）</p>
		<p>　　Java 提供了接口 java.lang.Runnable 来支持这种方法。</p>
		<p>　　方法二：实现 Runnable 接口</p>
		<p>　　Runnable接口只有一个方法run()，我们声明自己的类实现Runnable接口并提供这一方法，将我们的线程代码写入其中，就完成了这一部分的任务。但是Runnable接口并没有任何对线程的支持，我们还必须创建Thread类的实例，这一点通过Thread类的构造函数 public Thread(Runnable target);来实现。下面是一个例子：</p>
		<p>　　public class MyThread implements Runnable<br />　　{<br />　　int count= 1, number;<br />　　public MyThread(int num)<br />　　{<br />　　number = num;<br />　　System.out.println("创建线程 " + number);<br />　　}<br />　　public void run()<br />　　{<br />　　while(true)<br />　　{<br />　　System.out.println<br />　　("线程 " + number + ":计数 " + count);<br />　　if(++count== 6) return;<br />　　}<br />　　}<br />　　public static void main(String args[])<br />　　{<br />　　for(int i = 0; i 〈 5;<br />　　i++) new Thread(new MyThread(i+1)).start();<br />　　}<br />　　}<br />　　<br />　　严格地说，创建Thread子类的实例也是可行的，但是必须注意的是，该子类必须没有覆盖 Thread 类的 run 方法，否则该线程执行的将是子类的 run 方法，而不是我们用以实现Runnable 接口的类的 run 方法，对此大家不妨试验一下。</p>
		<p>　　使用 Runnable 接口来实现多线程使得我们能够在一个类中包容所有的代码，有利于封装，它的缺点在于，我们只能使用一套代码，若想创建多个线程并使各个线程执行不同的代码，则仍必须额外创建类，如果这样的话，在大多数情况下也许还不如直接用多个类分别继承 Thread 来得紧凑。</p>
		<p>　　综上所述，两种方法各有千秋，大家可以灵活运用。</p>
		<p>　　下面让我们一起来研究一下多线程使用中的一些问题。</p>
		<p>　　三、线程的四种状态</p>
		<p>　　1. 新状态：线程已被创建但尚未执行（start() 尚未被调用）。</p>
		<p>　　2. 可执行状态：线程可以执行，虽然不一定正在执行。CPU 时间随时可能被分配给该线程，从而使得它执行。</p>
		<p>　　3. 死亡状态：正常情况下 run() 返回使得线程死亡。调用 stop()或 destroy() 亦有同样效果，但是不被推荐，前者会产生异常，后者是强制终止，不会释放锁。</p>
		<p>　　4. 阻塞状态：线程不会被分配 CPU 时间，无法执行。</p>
		<p>　　四、线程的优先级</p>
		<p>　　线程的优先级代表该线程的重要程度，当有多个线程同时处于可执行状态并等待获得 CPU 时间时，线程调度系统根据各个线程的优先级来决定给谁分配 CPU 时间，优先级高的线程有更大的机会获得 CPU 时间，优先级低的线程也不是没有机会，只是机会要小一些罢了。</p>
		<p>　　你可以调用 Thread 类的方法 getPriority() 和 setPriority()来存取线程的优先级，线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间，缺省是5(NORM_PRIORITY)。</p>
		<p>　　五、线程的同步</p>
		<p>　　由于同一进程的多个线程共享同一片存储空间，在带来方便的同时，也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突，有效避免了同一个数据对象被多个线程同时访问。</p>
		<p>　　由于我们可以通过 private 关键字来保证数据对象只能被方法访问，所以我们只需针对方法提出一套机制，这套机制就是 synchronized 关键字，它包括两种用法：synchronized 方法和 synchronized 块。</p>
		<p>　　1. synchronized 方法：通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如：</p>
		<p>　　public synchronized void accessVal(int newVal);<br />　　<br />　　 synchronized 方法控制对类成员变量的访问：每个类实例对应一把锁，每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行，否则所属线程阻塞，方法一旦执行，就独占该锁，直到从该方法返回时才将锁释放，此后被阻塞的线程方能获得该锁，重新进入可执行状态。</p>
		<p>　　这种机制确保了同一时刻对于每一个类实例，其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态（因为至多只有一个能够获得该类实例对应的锁），从而有效避免了类成员变量的访问冲突（只要所有可能访问类成员变量的方法均被声明为 synchronized）。</p>
		<p>　　在 Java 中，不光是类实例，每一个类也对应一把锁，这样我们也可将类的静态成员函数声明为 synchronized ，以控制其对类的静态成员变量的访问。</p>
		<p>　　synchronized 方法的缺陷：若将一个大的方法声明为synchronized 将会大大影响效率，典型地，若将线程类的方法 run() 声明为 synchronized ，由于在线程的整个生命期内它一直在运行，因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中，将其声明为 synchronized ，并在主方法中调用来解决这一问题，但是 Java 为我们提供了更好的解决办法，那就是 synchronized 块。</p>
		<p>　　2. synchronized 块：通过 synchronized关键字来声明synchronized 块。语法如下：</p>
		<p>　　synchronized(syncObject)<br />　　{<br />　　//允许访问控制的代码<br />　　}<br />　　<br />　　synchronized 块是这样一个代码块，其中的代码必须获得对象 syncObject （如前所述，可以是类实例或类）的锁方能执行，具体机制同前所述。由于可以针对任意代码块，且可任意指定上锁的对象，故灵活性较高。</p>
		<p>　　六、线程的阻塞</p>
		<p>　　为了解决对共享存储区的访问冲突，Java 引入了同步机制，现在让我们来考察多个线程对共享资源的访问，显然同步机制已经不够了，因为在任意时刻所要求的资源不一定已经准备好了被访问，反过来，同一时刻准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题，Java 引入了对阻塞机制的支持。</p>
		<p>　　阻塞指的是暂停一个线程的执行以等待某个条件发生（如某资源就绪），学过操作系统的同学对它一定已经很熟悉了。Java 提供了大量方法来支持阻塞，下面让我们逐一分析。</p>
		<p>　　1. sleep() 方法：sleep() 允许指定以毫秒为单位的一段时间作为参数，它使得线程在指定的时间内进入阻塞状态，不能得到CPU 时间，指定的时间一过，线程重新进入可执行状态。典型地，sleep() 被用在等待某个资源就绪的情形：测试发现条件不满足后，让线程阻塞一段时间后重新测试，直到条件满足为止。</p>
		<p>　　2. suspend() 和 resume() 方法：两个方法配套使用，suspend()使得线程进入阻塞状态，并且不会自动恢复，必须其对应的resume() 被调用，才能使得线程重新进入可执行状态。典型地，suspend() 和 resume() 被用在等待另一个线程产生的结果的情形：测试发现结果还没有产生后，让线程阻塞，另一个线程产生了结果后，调用 resume() 使其恢复。</p>
		<p>　　3. yield() 方法：yield() 使得线程放弃当前分得的 CPU 时间，但是不使线程阻塞，即线程仍处于可执行状态，随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。</p>
		<p>　　4. wait() 和 notify() 方法：两个方法配套使用，wait() 使得线程进入阻塞状态，它有两种形式，一种允许指定以毫秒为单位的一段时间作为参数，另一种没有参数，前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态，后者则必须对应的 notify() 被调用。</p>
		<p>　　初看起来它们与 suspend() 和 resume() 方法对没有什么分别，但是事实上它们是截然不同的。区别的核心在于，前面叙述的所有方法，阻塞时都不会释放占用的锁（如果占用了的话），而这一对方法则相反。</p>
		<p>　　上述的核心区别导致了一系列的细节上的区别。</p>
		<p>　　首先，前面叙述的所有方法都隶属于 Thread 类，但是这一对却直接隶属于 Object 类，也就是说，所有对象都拥有这一对方法。初看起来这十分不可思议，但是实际上却是很自然的，因为这一对方法阻塞时要释放占用的锁，而锁是任何对象都具有的，调用任意对象的 wait() 方法导致线程阻塞，并且该对象上的锁被释放。</p>
		<p>　　而调用 任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞（但要等到获得锁后才真正可执行）。</p>
		<p>　　其次，前面叙述的所有方法都可在任何位置调用，但是这一对方法却必须在 synchronized 方法或块中调用，理由也很简单，只有在synchronized 方法或块中当前线程才占有锁，才有锁可以释放。</p>
		<p>　　同样的道理，调用这一对方法的对象上的锁必须为当前线程所拥有，这样才有锁可以释放。因此，这一对方法调用必须放置在这样的 synchronized 方法或块中，该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件，则程序虽然仍能编译，但在运行时会出现 IllegalMonitorStateException 异常。</p>
		<p>　　wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用，将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性：synchronized方法或块提供了类似于操作系统原语的功能，它们的执行不会受到多线程机制的干扰，而这一对方法则相当于 block 和wakeup 原语（这一对方法均声明为 synchronized）。</p>
		<p>　　它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法（如信号量算法），并用于解决各种复杂的线程间通信问题。关于 wait() 和 notify() 方法最后再说明两点：</p>
		<p>　　第一：调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的，我们无法预料哪一个线程将会被选择，所以编程时要特别小心，避免因这种不确定性而产生问题。</p>
		<p>　　第二：除了 notify()，还有一个方法 notifyAll() 也可起到类似作用，唯一的区别在于，调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然，只有获得锁的那一个线程才能进入可执行状态。</p>
		<p>　　谈到阻塞，就不能不谈一谈死锁，略一分析就能发现，suspend() 方法和不指定超时期限的 wait() 方法的调用都可能产生死锁。遗憾的是，Java 并不在语言级别上支持死锁的避免，我们在编程中必须小心地避免死锁。</p>
		<p>　　以上我们对 Java 中实现线程阻塞的各种方法作了一番分析，我们重点分析了 wait() 和 notify()方法，因为它们的功能最强大，使用也最灵活，但是这也导致了它们的效率较低，较容易出错。实际使用中我们应该灵活使用各种方法，以便更好地达到我们的目的。</p>
		<p>　　七、守护线程</p>
		<p>　　守护线程是一类特殊的线程，它和普通线程的区别在于它并不是应用程序的核心部分，当一个应用程序的所有非守护线程终止运行时，即使仍然有守护线程在运行，应用程序也将终止，反之，只要有一个非守护线程在运行，应用程序就不会终止。守护线程一般被用于在后台为其它线程提供服务。</p>
		<p>　　可以通过调用方法 isDaemon() 来判断一个线程是否是守护线程，也可以调用方法 setDaemon() 来将一个线程设为守护线程。</p>
		<p>　　八、线程组</p>
		<p>　　线程组是一个 Java 特有的概念，在 Java 中，线程组是类ThreadGroup 的对象，每个线程都隶属于唯一一个线程组，这个线程组在线程创建时指定并在线程的整个生命期内都不能更改。</p>
		<p>　　你可以通过调用包含 ThreadGroup 类型参数的 Thread 类构造函数来指定线程属的线程组，若没有指定，则线程缺省地隶属于名为 system 的系统线程组。</p>
		<p>　　在 Java 中，除了预建的系统线程组外，所有线程组都必须显式创建。在 Java 中，除系统线程组外的每个线程组又隶属于另一个线程组，你可以在创建线程组时指定其所隶属的线程组，若没有指定，则缺省地隶属于系统线程组。这样，所有线程组组成了一棵以系统线程组为根的树。</p>
		<p>　　Java 允许我们对一个线程组中的所有线程同时进行操作，比如我们可以通过调用线程组的相应方法来设置其中所有线程的优先级，也可以启动或阻塞其中的所有线程。</p>
		<p>　　Java 的线程组机制的另一个重要作用是线程安全。线程组机制允许我们通过分组来区分有不同安全特性的线程，对不同组的线程进行不同的处理，还可以通过线程组的分层结构来支持不对等安全措施的采用。</p>
		<p>　　Java 的 ThreadGroup 类提供了大量的方法来方便我们对线程组树中的每一个线程组以及线程组中的每一个线程进行操作。</p>
		<p>　　九、总结</p>
		<p>　　在本文中，我们讲述了 Java 多线程编程的方方面面，包括创建线程，以及对多个线程进行调度、管理。我们深刻认识到了多线程编程的复杂性，以及线程切换开销带来的多线程程序的低效性，这也促使我们认真地思考一个问题：我们是否需要多线程？何时需要多线程？</p>
		<p>　　多线程的核心在于多个代码块并发执行，本质特点在于各代码块之间的代码是乱序执行的。我们的程序是否需要多线程，就是要看这是否也是它的内在特点。</p>
		<p>　　假如我们的程序根本不要求多个代码块并发执行，那自然不需要使用多线程；假如我们的程序虽然要求多个代码块并发执行，但是却不要求乱序，则我们完全可以用一个循环来简单高效地实现，也不需要使用多线程；只有当它完全符合多线程的特点时，多线程机制对线程间通信和线程管理的强大支持才能有用武之地，这时使用多线程才是值得的。<br /></p>
<img src ="http://www.blogjava.net/endisoft/aggbug/71209.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/endisoft/" target="_blank">Endisoft</a> 2006-09-21 22:46 <a href="http://www.blogjava.net/endisoft/archive/2006/09/21/71209.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>