??xml version="1.0" encoding="utf-8" standalone="yes"?> 1QDuplicated Code 4QDivergent Change
试用地址Q?a >http://imaiyi.com/tryhtmleditor.htm
来几个截图:(x)
代码重复几乎是最常见的异味了。他也是Refactoring 的主要目标之一。代码重复往
往来自于copy-and-paste 的编E风根{与他相对应OAOO 是一个好pȝ的重要标?br />Q请参见我的duplicated code 一文:(x)http://www.erptao.org/download.php?op=viewsdownload&sid=6Q?/p>
2QLong method
它是传统l构化的“遗毒“。一个方法应当具有自我独立的意图Q不要把几个意图
攑֜一P我的《大cd长方法》一文中有详l描q?/p>
3QLarge Class
大类是你把太多的责Ml了一个类。这里的规则是One Class One Responsibility?/p>
一个类里面的内容变化率不同。某些状态一个小时变一ơ,某些则几个月一q才?br />一ơ;某些状态因斚w的原因发生变化,而另一些则因ؓ(f)其他斚w的原因变一ơ?br />面向对象的抽象就是把相对不变的和相对变化盔RR把问题变化的一斚w和另一
斚w盔RR这使得q些相对不变的可以重用。问题变化的每个斚w都可以单独重用?br />q种相异变化的共存得重用非常困难?/p>
5QShotgun Surgery
q正好和上面相反。对pȝ一个地方的改变涉及(qing)到其他许多地方的相关改变。这?br />变化率和变化内容怼的状态和行ؓ(f)通常应当攑֜同一个类中?/p>
6QFeature Envy
对象的目的就是封装状态以?qing)与q些状态紧密相关的行ؓ(f)。如果一个类的方法频J?br />用get Ҏ(gu)存取其他cȝ状态进行计,那么你要考虑把行为移到涉?qing)状态数目最多的
那个cR?/p>
7QData Clumps
某些数据通常像孩子一h玩耍:(x)一起出现在很多cȝ成员变量中,一起出现在
许多Ҏ(gu)的参C…..Q这些数据或许应该自q立Ş成对象?/p>
8QPrimitive Obsession
面向对象的新手通常?fn)惯使用几个原始cd的数据来表示一个概c(din)譬如对于范_(d)
他们?x)用两个数字。对于MoneyQ他们会(x)用一个QҎ(gu)来表C。因Z没有使用对象
来表N题中存在的概念,q得代码变的难以理解,解决问题的难度大大增加?br />好的?fn)惯是扩充语a所能提供原始类型,用小对象来表C围、金额、{化率、邮
政编码等{?/p>
9QSwitch Statement
Z帔R的开兌句是OO 的大敌,你应当把他变为子cRstate 或strategy?/p>
10Q?Parallel Inheritance Hierarchies
q行的承层ơ是shotgun surgery 的特D情c(din)因为当你改变一个层ơ中的某一?br />cLQ你必须同时改变另外一个层ơ的q行子类?/p>
11Q?Lazy Class
一个干zM多的cR类的维护需要额外的开销Q如果一个类承担了太的责QQ应
当消除它?/p>
12Q?Speculative Generality
一个类实现了从未用到的功能和通用性。通常q样的类或方法唯一的用htest
case。不要犹豫,删除它?/p>
13Q?Temporary Field
一个对象的属性可能只在某些情况下才有意义。这L(fng)代码难以理解。专门徏?br />一个对象来持有q样的孤儿属性,把只和他相关的行为移到该cR最常见的是一个特?br />的算法需要某些只有该法才有用的变量?/p>
14Q?Message Chain
消息铑֏生于当一个客户向一个对象要求另一个对象,然后客户又向q另一对象?br />求另一个对象,再向q另一个对象要求另一个对象,如此如此。这Ӟ你需要隐藏分z?/p>
15Q?Middle Man
对象的基本特性之一是装Q而你l常?x)通过分派d现封装。但是这一步不能走得太q,如果你发C个类接口的一大半Ҏ(gu)都在做分z,你可能需要移去这个中?br />人?/p>
16Q?Inappropriate Intimacy
某些cȝ互之间太亲密Q它们花费了太多的时间去砖研别h的私有部分。对人类?br />aQ我们也怸应该太假正经Q但我们应当让自qcM格遵守禁Ʋ主义?/p>
17Q?Alternative Classes with Different Interfaces
做相同事情的Ҏ(gu)有不同的函数signatureQ一致把它们往cdơ上U,直至协议一
致?/p>
18Q?Incomplete Library Class
要徏立一个好的类库非常困难。我们大量的E序工作都基于类库实现。然而,如此
q泛而又相异的目标对库构提Z苛刻的要求。库构徏者也不是万能的。有时候我
们会(x)发现库类无法实现我们需要的功能。而直接对库类的修Ҏ(gu)非常困难。这时候就需
要用各种手段q行Refactoring?/p>
19Q?Data Class
对象包括状态和行ؓ(f)。如果一个类只有状态没有行为,那么肯定有什么地方出问题
了?/p>
20Q?Refused Bequest
类传下来很多行为和状态,而子cd是用了其中的很小一部分。这通常意味着?br />的类层次有问题?/p>
21Q?Comments
l常觉得要写很多注释表示你的代码难以理解。如果这U感觉太多,表示你需?br />Refactoring?/p> 卖艺|提?/a>
XP是勇气,交流Q反馈和单?br />XP是Y件开发过E中的纪律,它规定你Q必d~程前些试Q必M个h一L(fng)E,必须遵守~程规范……?br />XP是把最好的实践l验提取出来QŞ成了一个崭新的开发方法?/p>
2. XP适用范围:
极限~程Q也被叫做XPQ适用于中型团队在需求不明确或者迅速变化的情况下进行Y件开发的轻量U方法学?br />推荐使用范围?0人左右的团队
3.XP工作模式体现:
一、工作环?br />二、立式晨?br />三、结对编E?br />四、测试驱动开?br />五、重?br />六、持l集?br />七、频J地发布?yu)版?/p>
4.l对~程:
开发Q务会(x)l化分解为很多TaskQ一个Task的开发周期一般不过2天?br />每个Task的Owner?x)寻找一个Partnerq行l对开发?br />Task开发的ơ序q序员们自己协商。他可以先作为Partner和其他Owner一起开发某个TaskQ然后再扑֏一个程序员作ؓ(f)Partner来共同开发自己承担的Task?br />l对开发时QTask的Owner主要负责~码Q?Partner负责在一旁看Owner~程q在其编写有错误提出自己的意见,当其遇到困难时一赯论、互相帮助完成Q?/p>
5.试驱动开?
在动手编码之前,必须先写功能试脚本、单元测试脚本?br />写好试脚本后,开始编码、重构、运行单元测试、集成、运行功能测试,以此循环
6.重构:
减少重复设计Q优化设计结构,提高技术上的重用性和可扩展性?br />XP提倡毫不留情的重构?br />M人可以重构Q何代码,前提是重构后的代码一定要通过100%试单元试后才能被Check-in
7.持箋集成:
试先行是持l集成的一个重要前提?br />持箋集成指不断地把完成的功能模块整合在一赗目的在于不断获得客户反馈以?qing)尽早发现BUG?br />随时整合Q越频繁好Q集成及(qing)试q程的自动化E度高好?br />每次只有一个新增加部分在整合,而且必须q行功能试
8.频繁地发布小版本:
发布q程应该可能地自动化、规范化?br />不断地发布可用的pȝ可以告诉客户你在做正的事情?br />客户使用发布的系l,可以保证频繁地反馈和交流?br />保证客户有够的依据调控开发过E?增加、删除或改变需??br />降低开发风险?br />随着开发的推进Q发布越来越频繁?br />所有的发布都要l过功能试?/p>
9.XP的关键词:
试优先原则
l对~程
持箋集成
频繁版?br />不断重构
立式晨会(x)
交流和沟通,“只有没有沟通不够的目Q没有沟通过度的目”
分解d、制定计划是关键一?/p>
10.XP作用:
一、^E的工作效率
q稳的工作效率指团队和个人在很长的时期内保持一定的开发效率?br />保证了项目速度和计划过E的有效性和准确性;
保证了程序员可以持箋地完成Q务,团队可以持箋地向客户交付可运行的pȝQ?br />l对~程已经加大了工作强度,q且和其它XP的规则一h高了工作效率Qɞ加班和l持q稳的工作效率可能而且可行?br />提倡^E的工作效率Q体CXP以h为本的h(hun)D?br />二、高质量
试优先、ƈ坚持单元试、每个版本进行功能测试的原则是保证了高质量的一个关键;
充分的沟通交进一步减了写低质量代码的风险;
l对开发模式在互相学习(fn)中会(x)产出高质量的代码
三、Open
l对开发、每一处修攚w需要测试等{规则得实现集体拥有代码, “我们”的代码,而不?#8220;?#8221;的代码;
充分的沟通交可以将每个人的知识、思想׃nQ?br />让每个h都知道项目的设计、计划、进展情늭信息Q?br />大家都知道每个h都在做什么和怎么做;
四、对人的挑战
暴露自己的缺点,人的本?br />懒惰
自尊
闭
……
克服自己的缺?br />高效?br />不怕告诉别׃?x),乐于问h
懂得重别hQ乐于帮助别?br />……
11.受益于XP:
一个曾l在XP模式下工作过的hQ回Cl开发模式下才深M?x)到XPl他带来的胦(ch)富?br />在传l开发模式下他坚持每天有计划、ȝQ坚持测试驱动开?#8230;…
发现他L按时下班甚至提前下班Q可是同事们来多且越来越晚下班,是自׃认真Q是同事们爱表现Q?#8230;…
都不是!Q?br />是XPl他带来的受益终w的开发方式,他的同事bug量远q比他多Q他只有不多的几?同事们Q务L延时Q而自己都是轻松按时完?#8230;…
首先是RUPQRUP有什么特点呢QP代性开发,用例驱动Q用UML对Y件徏模,提倡事先设计好以组件ؓ(f)核心的体pȝ?以体pȝ构ؓ(f)中心)Q不断的评估和测试Y件质量,Q用用例)控制软g的变化。在q些原则的基上,把Y件h员分成各U角?分析Q开发,理Q测试,工具支持Q配|?{等Q在软g开发过E中的各U品叫做工?Artifact)?/p>
再看TSPQTSP把h员分成小l领D、开发经理、计划经理、质量/生l理Q以?qing)技术支持经?注意q点和RUP的雷同),要求各个人员严格记录在Y件开发过E中的每一步,比如E序的Bug率,使用的时间等{?/p>
最后一个是XPQXP的特点,双h~程Q简单用?User Story)QRefactoringQ以周ؓ(f)基础的P代。持l集成,现场客户Q测试驱动,自动化测试,代码同步。同L(fng)QXP也把人员分成试Q交h员,设计师,技术作者,客户Q程序员{等?/p>
OKQ说了这么多长篇大论Q是Z把几个Y件过E拿出来比较。所有的软gq程无非是ؓ(f)了避免风险,保证目的成功?/p>
拿交通工具做比方的话QTSP好比坐火RQ由于TSP是CMM那帮子h搞的Q所以TSP不强调P代性开发。TSP的是质量理和控Ӟ通过每个觉自愿的记录每天的行为,从而的得到严格的项目的数据Q缺乏了q种严格控制QTSP好比火车没有轨道,一点用处也没有。而在我们实验室一帮懒懒散散的学生中搞数据Q要他们每天填表Q从而知道项目消耗了多少人时QBug率ؓ(f)多少Q不要做梦了吧,所以TSP那套方式肯定是行不通的?/p>
再看XP和RUP的差别,q代性开发,两者都Q不q两者的含义不同Q在RUP中,每次q代Q强调生的是一个个工gQ比如完成了用例。而在XP中,产生的是可用的Y件。ؓ(f)什么RUP的P代里面可能没有生可用的软g呢?因ؓ(f)RUP的是用例驱动和体pȝ构ؓ(f)中心Q所以RUP花在设计体系l构和用例上的时间会(x)比较多,q样带来的好处是软g的后期变更会(x)比较?yu)。而XP本n的是拥抱变化Q不三七二十一Q先开发出来一个能用的再说Q如果客户不满意Q别忘了QXP是现场客PQRefactoring之。所以在XP的开头的时候,Ҏ(gu)׃提倡太复杂的用例(客户在现场嘛Q不懂客L(fng)意思,现场交流啊)Q也不提倡过多的设计Q测试驱动嘛Q通不q的话Refactoring之)?/p>
然而RUP没有现场客户的概念,所以清晰的用例描述是RUP中很重要的一环。只有这些用例在客户和团队之间达成了pQ才能做下一步的工作Q同L(fng)Q需求的变更也必通过用例来体玎ͼRUP很强调变更管理,是q个意思?/p>
而在我们实验室做现在q个目Q不是和客户交流的问题,而是没有客户Q!Q?/p>
所以,在这U程度下Q我们的用例Q不是要让客L(fng)解,而是我们自己理解p够了。而体pȝ构,׃你们现在不用考虑分布式,q发Q事务等{一pd东西。这些东襉K由J2EE做了?br />
此外RUP在我们实验室很难办的一件事情是对各个阶D生的工g的质量监控,同学们互相哈哈哈Q很隑֯一个文性的东西q行评h(hun)?br />
那么要改善我们现在做的项目,最重要的是做什么呢Q第一是,P代,我们现在整个软g开发周期太长了Q应该羃短到2Q?周以内。第二,试Q我们现在的试很多都是手动的,需要自动化q个试q程。第三是Q快速构建,持箋集成。整个Y件的部v周期不能像现在这么长Q不能由同学们手工构建,而必L自动化的部v。这些都是在XP中强调的?br />
RUP的不同就好比是做BUS和自己开汽车的不同Q尽细微,但是Q开汽车更需要小心翼的调整方向Q而公交R毕竟有线路?br />
如果在一个大公司做部门经理的话,我更愿意采用RUP那套方式Q辅之于XP的各U实践,然而在实验室,我只有退而求其次Q因地制宜,XP能推q多是多少Q一些很难推q的东西比如风险理QBUG理只能暂时攑ּ了?/p>
public class MyThread_1 extends Thread
{
public void run()
{
//some code
}
}
eg.实现Runnable接口
public class MyThread_2 implements Runnable
{
public void run()
{
//some code
}
}
当用承创建线E,q样启动U程Q?br />
new MyThread_1().start()
当用实现接口创建线E,q样启动U程Q?br />
new Thread(new MyThread_2()).start()
注意Q其实是创徏一个线E实例,q以实现了Runnable接口的类为参C入这个实例,当执行这个线E的时候,MyThread_2中run里面的代码将被执行?br />下面是完成的例子Q?br />
public class MyThread implements Runnable
{
public void run()
{
System.out.println("My Name is "+Thread.currentThread().getName());
}
public static void main(String[] args)
{
new Thread(new MyThread()).start();
}
}
执行后将打印出:(x)
My Name is Thread-0
你也可以创徏多个U程Q像下面q样
new Thread(new MyThread()).start();
new Thread(new MyThread()).start();
new Thread(new MyThread()).start();
那么?x)打印出Q?br />My Name is Thread-0
My Name is Thread-1
My Name is Thread-2
看了上面的结果,你可能会(x)认ؓ(f)U程的执行顺序是依次执行的,但是那只是一般情况,千万不要用以为是U程的执行机Ӟ影响U程执行序的因素有几点Q首先看看前面提到的优先U别
public class MyThread implements Runnable
{
public void run()
{
System.out.println("My Name is "+Thread.currentThread().getName());
}
public static void main(String[] args)
{
Thread t1=new Thread(new MyThread());
Thread t2=new Thread(new MyThread());
Thread t3=new Thread(new MyThread());
t2.setPriority(Thread.MAX_PRIORITY);//赋予最高优先
t1.start();
t2.start();
t3.start();
}
}
再看看结果:(x)
My Name is Thread-1
My Name is Thread-0
My Name is Thread-2
U程的优先分ؓ(f)10U,分别??0的整C表,默认情况?。上面的t2.setPriority(Thread.MAX_PRIORITY){h(hun)与t2.setPriority(10Q?br />然后是线E程序本w的设计Q比如用sleep,yield,joinQwait{方法(详情L(fng)JDKDocument)
public class MyThread implements Runnable
{
public void run()
{
try
{
int sleepTime=(int)(Math.random()*100);//产生随机数字Q?br />Thread.currentThread().sleep(sleepTime);//让其休眠一定时_(d)旉又上面sleepTime军_
//public static void sleep(long millis)throw InterruptedException QAPIQ?br />System.out.println(Thread.currentThread().getName()+" 睡了 "+sleepTime);
}catch(InterruptedException ie)//׃U程在休眠可能被中断Q所以调用sleepҎ(gu)的时候需要捕捉异?br />{
ie.printStackTrace();
}
}
public static void main(String[] args)
{
Thread t1=new Thread(new MyThread());
Thread t2=new Thread(new MyThread());
Thread t3=new Thread(new MyThread());
t1.start();
t2.start();
t3.start();
}
}
执行后观察其输出Q?br />
Thread-0 睡了 11
Thread-2 睡了 48
Thread-1 睡了 69
上面的执行结果是随机的,再执行很可能出现不同的结果。由于上面我在run中添加了休眠语句Q当U程休眠的时候就?x)让出cpuQcpu会(x)选择执行处于runnable状态中的其他线E,当然也可能出现这U情况,休眠的Thread立即q入了runnable状态,cpu再次执行它?br />[U程l概念]
U程是可以被l织的,java中存在线E组的概念,每个U程都是一个线E组的成?U程l把多个U程集成Z个对?通过U程l可以同时对其中的多个线E进行操?如启动一个线E组的所有线E等.Java的线E组由java.lang包中的Thread——Groupcd?
ThreadGroupcȝ来管理一l线E?包括:U程的数?U程间的关系,U程正在执行的操?以及(qing)U程要启动或终止时间等.U程l还可以包含U程l?在Java的应用程序中,最高层的线E组是名位main的线E组,在main中还可以加入U程或线E组,在mian的子U程l中也可以加入线E和U程l?形成U程l和U程之间的树(wi)状承关pR像上面创徏的线E都是属于mainq个U程l的?br />借用上面的例子,main里面可以q样写:(x)
public static void main(String[] args)
{
/***************************************
ThreadGroup(String name)
ThreadGroup(ThreadGroup parent, String name)
***********************************/
ThreadGroup group1=new ThreadGroup("group1");
ThreadGroup group2=new ThreadGroup(group1,"group2");
Thread t1=new Thread(group2,new MyThread());
Thread t2=new Thread(group2,new MyThread());
Thread t3=new Thread(group2,new MyThread());
t1.start();
t2.start();
t3.start();
}
U程l的嵌套Qt1,t2,t3被加入group2,group2加入group1?br />另外一个比较多是关于U程同步斚w的,试想q样一U情况,你有一W存?gu)Ƒ֜银行Q你在一安行ؓ(f)你的账户存款Q而你的妻子在另一安行从q个账户提款Q现在你?000块在你的账户里面。你存入?000Q但是由于另一方也在对q笔存款q行操作Qh家开始执行的时候只看到账户里面原来?000元,当你的妻子提?000元后Q你d所在的银行pZ的̎户里面没有钱了,而你所在的银行却认Zq有2000元?br />看看下面的例子:(x)
class BlankSaving //储蓄账户
{
private static int money=10000;
public void add(int i)
{
money=money+i;
System.out.println("Husband 向银行存入了 [K?+i+"]");
}
public void get(int i)
{
money=money-i;
System.out.println("Wife 向银行取C [K?+i+"]");
if(money<0)
System.out.println("余额不Q?);
}
public int showMoney()
{
return money;
}
}
class Operater implements Runnable
{
String name;
BlankSaving bs;
public Operater(BlankSaving b,String s)
{
name=s;
bs=b;
}
public static void oper(String name,BlankSaving bs)
{
if(name.equals("husband"))
{
try
{
for(int i=0;i<10;i++)
{
Thread.currentThread().sleep((int)(Math.random()*300));
bs.add(1000);
}
}catch(InterruptedException e){}
}else
{
try
{
for(int i=0;i<10;i++)
{
Thread.currentThread().sleep((int)(Math.random()*300));
bs.get(1000);
}
}catch(InterruptedException e){}
}
}
public void run()
{
oper(name,bs);
}
}
public class BankTest
{
public static void main(String[] args)throws InterruptedException
{
BlankSaving bs=new BlankSaving();
Operater o1=new Operater(bs,"husband");
Operater o2=new Operater(bs,"wife");
Thread t1=new Thread(o1);
Thread t2=new Thread(o2);
t1.start();
t2.start();
Thread.currentThread().sleep(500);
}
}
下面是其中一ơ的执行l果Q?br />
---------first--------------
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Husband 向银行存入了 [K?000]
看到了吗Q这可不是正的需求,在husbandq没有结束操作的时候,wife插了进来,q样很可能导致意外的l果。解军_法很单,是对数据q行操作Ҏ(gu)声明为synchronized,当方法被该关键字声明后,也就意味着Q如果这个数据被加锁Q只有一个对象得到这个数据的锁的时候该对象才能对这个数据进行操作。也是当你存款的时候,q笔账户在其他地Ҏ(gu)不能q行操作的,只有你存?gu)Ƒ֮毕,银行理人员̎戯锁,其他人才能对q个账户q行操作?br />修改public static void oper(String name,BlankSaving bs)为public static void oper(String name,BlankSaving bs)Q再看看l果:
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Husband 向银行存入了 [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
Wife 向银行取C [K?000]
当丈夫完成操作后Q妻子才开始执行操作,q样的话Q对׃n对象的操作就不会(x)有问题了?br />[wait and notify]
你可以利用这两个Ҏ(gu)很好的控制线E的执行程Q当U程调用waitҎ(gu)后,U程被挂vQ直到被另一U程唤醒QnotifyQ或则是如果waitҎ(gu)指定有时间得话,在没有被唤醒的情况下Q指定时间时间过后也自动被唤醒。但是要注意一定,被唤醒ƈ不是指马上执行,而是从组塞状态变为可q行状态,其是否运行还要看cpu的调度?br />事例代码Q?br />
class MyThread_1 extends Thread
{
Object lock;
public MyThread_1(Object o)
{
lock=o;
}
public void run()
{
try
{
synchronized(lock)
{
System.out.println("Enter Thread_1 and wait");
lock.wait();
System.out.println("be notified");
}
}catch(InterruptedException e){}
}
}
class MyThread_2 extends Thread
{
Object lock;
public MyThread_2(Object o)
{
lock=o;
}
public void run()
{
synchronized(lock)
{
System.out.println("Enter Thread_2 and notify");
lock.notify();
}
}
}
public class MyThread
{
public static void main(String[] args)
{
int[] in=new int[0];//notice
MyThread_1 t1=new MyThread_1(in);
MyThread_2 t2=new MyThread_2(in);
t1.start();
t2.start();
}
}
执行l果如下Q?br />Enter Thread_1 and wait
Enter Thread_2 and notify
Thread_1 be notified
可能你注意到了在使用wait and notifyҎ(gu)得时候我使用了synchronized块来包装q两个方法,q是׃调用q两个方法的时候线E必获得锁Q也是上面代码中的lock[]Q如果你不用synchronized包装q两个方法的得话Q又或则锁不一是同一把,比如在MyThread_2中synchronized(lock)改ؓ(f)synchronized(this),那么执行q个E序的时候将?x)抛出java.lang.IllegalMonitorStateException执行期异常。另外wait and notifyҎ(gu)是Object中的Qƈ不在Threadq个cM。最后你可能注意Cq点Qint[] in=new int[0];Z么不是创建new Object而是一?长度的数l,那是因ؓ(f)在java中创Z?长度的数l来充当锁更加高效?br />
Thread作ؓ(f)java中一重要l成部分Q当然还有很多地斚w要更深刻的认识,上面只是对Thread的一些常识和易错问题做了一个简要的ȝQ若要真正的掌握java的线E,q需要自己多做ȝ
notify()?qing)notifyAll()是Object的方?与Object的wait()Ҏ(gu)配合使用,而且q三个方法必d同步块中调用.
如下:
在线E?中执行如下代?br />...
synchronized(obj1) //1.q入同步?br />{
try {
...
obj1.wait(); //2.q入暂停状?br /> }catch (InterruptedException exp) {}
}
1.当前同步对象(monitor)为obj1,obj1是Q一实例,若是同步Ҏ(gu),则同步对象是this.q入同步块后,obj1为锁定状?锁定状态对obj1本n无Q何媄(jing)?而其它线E执行到同一代码?因不能获得锁,处于Block状?一旦解?被block的线E自动l执?
2.调用obj1.wait()有两个作?一是ɾU程q入{待状?二是解锁obj1,q时被block的线E将获得?U程1要退出等待必要由其它线E显式的调用obj1.notify()或notifyAll()Ҏ(gu).
?/p>
...
synchronized(obj1)
{
...
obj1.notify(); //3. 向wait的线E发通知信息
...
}
...
若其它线E执行到此时,U程1处于wait状?则wait状态解?解除?若线E?若得到锁pl箋执行,若有多个U程处于obj1的wait状?notify随机选一个线E激z?而notifyAll是同时解除所有的wait状?
notifyAll()让等待某个对象K的所有线E离开d状态,
notify()随机地选取{待某个对象K的线E,让它dd状态?br />
notify(),notifyAll()非常有用Q在一个synchronized(lockObj)块中当调用lockObj.wait()ӞU程已l将锁放开来了Q这时当另外一个线E调用lockObj.notify()Ӟ{待的线E就?x)l执行下M。这是一U非帔R效的U程同步机制。如果没有他Q用sleep()来同步的话就太浪Ҏ(gu)间了?br />一个简单的例子Q?br />thread1 receive data
thread2 pase received data
lockObj是buf
当buf中没有数据时Qthread2中调用buf.wait释放锁,让thread1有机?x)执行?br />当thread1收到数据Ӟ调用buf.notify()通知thread1d理收到的数据?br />
如果在同步块入口炚w?不须其它U程调用notify(),调了也没效果,同步块能自动获得?/p>
如果是wait造成了阻?ȝnotfiy()Ȁz?q两个方法是配合使用
notify、notifyAll、wait :
主要是ؓ(f)了解x有监视器钥匙的线E暂停等待另一U程完成时可能发生死锁的问题。wait()Ҏ(gu)使调用线E等待,直到发生时或另一U程调用同一对象的notifyQ)或notifyAll()Ҏ(gu)。wait() Ҏ(gu)的用法如下:(x)wait()或wait(long timeoutPeriodInMilliseconds)前者等待被通知Q后者等待超时或通知。线E调用wait()Ҏ(gu)Ӟ释放所持有的钥匙让另一{待U程q入监视区。notify()Ҏ(gu)只唤醒一个等待线E,而notifyAll()唤醒所有等待该监视器的U程。注意,q些Ҏ(gu)只能在监视区内调用。否则会(x)输出一URuntimeExceptioncd的IllegaMonitorStateException异常状态?br />够详l清楚的吧?br /> Mwait()让线E等待,notify()和notifyall()ȀzL个等待线E,其实是撤销该线E的中断状态,从而他们有机?x)再ơ运?br /> 有时?x)遇到如下问?E序的一部分在写数据,另一部分L?但有时会(x)出现读部分超前写部分,
q就是典型的产生?消耗者问?
.wait:是一个线E睡?直到在相同的对象上调用notify或notifyAll
.notify:启动W一个在相同对象调用wait的线E?br /> .notifyAll:启动在相同的对象调用wait的所有线E?/p>
Collection接口
Collection是最基本的集合接口,一个Collection代表一lObjectQ即Collection的元素(ElementsQ。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接承自Collection的类QJava SDK提供的类都是l承自Collection的“子接口”如List和Set?br /> 所有实现Collection接口的类都必L供两个标准的构造函敎ͼ(x)无参数的构造函数用于创Z个空的CollectionQ有一个Collection参数的构造函数用于创Z个新的CollectionQ这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection?br /> 如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个P代子Q用该q代子即可逐一讉KCollection中每一个元素。典型的用法如下Q?br /> Iterator it = collection.iterator(); // 获得一个P代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一个元?br /> }
由Collection接口z的两个接口是List和Set?/p>
List接口
List是有序的CollectionQ用此接口能够_的控制每个元素插入的位置。用戯够用烦(ch)引(元素在List中的位置Q类g数组下标Q来讉KList中的元素Q这cM于Java的数l?br />和下面要提到的Set不同QList允许有相同的元素?br /> 除了hCollection接口必备的iterator()Ҏ(gu)外,Listq提供一个listIterator()Ҏ(gu)Q返回一个ListIterator接口Q和标准的Iterator接口相比QListIterator多了一些add()之类的方法,允许dQ删除,讑֮元素Q还能向前或向后遍历?br /> 实现List接口的常用类有LinkedListQArrayListQVector和Stack?/p>
LinkedListc?/strong>
LinkedList实现了List接口Q允许null元素。此外LinkedList提供额外的getQremoveQinsertҎ(gu)在LinkedList的首部或N。这些操作LinkedList可被用作堆栈QstackQ,队列QqueueQ或双向队列QdequeQ?br /> 注意LinkedList没有同步Ҏ(gu)。如果多个线E同时访问一个ListQ则必须自己实现讉K同步。一U解x法是在创建List时构造一个同步的ListQ?br /> List list = Collections.synchronizedList(new LinkedList(...));
ArrayListc?/strong>
ArrayList实现了可变大的数组。它允许所有元素,包括null。ArrayList没有同步?br />sizeQisEmptyQgetQsetҎ(gu)q行旉为常数。但是addҎ(gu)开销为分摊的常数Q添加n个元素需要O(n)的时间。其他的Ҏ(gu)q行旉为线性?br /> 每个ArrayList实例都有一个容量(CapacityQ,即用于存储元素的数组的大。这个容量可随着不断d新元素而自动增加,但是增长法q没有定义。当需要插入大量元素时Q在插入前可以调用ensureCapacityҎ(gu)来增加ArrayList的容量以提高插入效率?br /> 和LinkedList一PArrayList也是非同步的QunsynchronizedQ?/p>
Vectorc?/strong>
Vector非常cMArrayListQ但是Vector是同步的。由Vector创徏的IteratorQ虽然和ArrayList创徏的Iterator是同一接口Q但是,因ؓ(f)Vector是同步的Q当一个Iterator被创且正在被用,另一个线E改变了Vector的状态(例如Q添加或删除了一些元素)Q这时调用Iterator的方法时抛出ConcurrentModificationExceptionQ因此必L莯异常?/p>
Stack c?/font>
Stackl承自VectorQ实C个后q先出的堆栈。Stack提供5个额外的Ҏ(gu)使得Vector得以被当作堆栈用。基本的push和popҎ(gu)Q还有peekҎ(gu)得到栈顶的元素,emptyҎ(gu)试堆栈是否为空QsearchҎ(gu)(g)一个元素在堆栈中的位置。Stack刚创建后是空栈?/p>
Set接口
Set是一U不包含重复的元素的CollectionQ即L的两个元素e1和e2都有e1.equals(e2)=falseQSet最多有一个null元素?br /> 很明显,Set的构造函数有一个约束条Ӟ传入的Collection参数不能包含重复的元素?br /> h意:(x)必须心操作可变对象QMutable ObjectQ。如果一个Set中的可变元素改变了自w状态导致Object.equals(Object)=true导致一些问题?/p>
Map接口
h意,Map没有l承Collection接口QMap提供key到value的映。一个Map中不能包含相同的keyQ每个key只能映射一个value。Map接口提供3U集合的视图QMap的内容可以被当作一lkey集合Q一lvalue集合Q或者一lkey-value映射?/p>
Hashtablec?/strong>
Hashtablel承Map接口Q实C个key-value映射的哈希表。Q何非I(non-nullQ的对象都可作ؓ(f)key或者value?br /> d数据使用put(key, value)Q取出数据用get(key)Q这两个基本操作的时间开销为常数?br />Hashtable通过initial capacity和load factor两个参数调整性能。通常~省的load factor 0.75较好地实C旉和空间的均衡。增大load factor可以节省I间但相应的查找旉增大,q会(x)影响像get和putq样的操作?br />使用Hashtable的简单示例如下,?Q?Q?攑ֈHashtable中,他们的key分别是”one”,”two”,”three”:(x)
Hashtable numbers = new Hashtable();
numbers.put(“one? new Integer(1));
numbers.put(“two? new Integer(2));
numbers.put(“three? new Integer(3));
要取Z个数Q比?Q用相应的keyQ?br /> Integer n = (Integer)numbers.get(“two?;
System.out.println(“two = ?+ n);
׃作ؓ(f)key的对象将通过计算其散列函数来定与之对应的value的位|,因此M作ؓ(f)key的对象都必须实现hashCode和equalsҎ(gu)。hashCode和equalsҎ(gu)l承自根cObjectQ如果你用自定义的类当作key的话Q要相当心Q按照散列函数的定义Q如果两个对象相同,即obj1.equals(obj2)=trueQ则它们的hashCode必须相同Q但如果两个对象不同Q则它们的hashCode不一定不同,如果两个不同对象的hashCode相同Q这U现象称为冲H,冲突?x)导致操作哈希表的时间开销增大Q所以尽量定义好的hashCode()Ҏ(gu)Q能加快哈希表的操作?br /> 如果相同的对象有不同的hashCodeQ对哈希表的操作?x)出现意想不到的l果Q期待的getҎ(gu)q回nullQ,要避免这U问题,只需要牢C条:(x)要同时复写equalsҎ(gu)和hashCodeҎ(gu)Q而不要只写其中一个?br /> Hashtable是同步的?/p>
HashMapc?/font>
HashMap和HashtablecMQ不同之处在于HashMap是非同步的,q且允许nullQ即null value和null key。,但是HashMap视ؓ(f)CollectionӞvalues()Ҏ(gu)可返回CollectionQ,其P代子操作旉开销和HashMap的容量成比例。因此,如果q代操作的性能相当重要的话Q不要将HashMap的初始化定w讑־q高Q或者load factorq低?/p>
WeakHashMapc?/strong>
WeakHashMap是一U改q的HashMapQ它对key实行“弱引用”,如果一个key不再被外部所引用Q那么该key可以被GC回收?/p>
ȝ
如果涉及(qing)到堆栈,队列{操作,应该考虑用ListQ对于需要快速插入,删除元素Q应该用LinkedListQ如果需要快速随问元素,应该使用ArrayList?br /> 如果E序在单U程环境中,或者访问仅仅在一个线E中q行Q考虑非同步的c,其效率较高,如果多个U程可能同时操作一个类Q应该用同步的cR?br /> 要特别注意对哈希表的操作Q作为key的对象要正确复写equals和hashCodeҎ(gu)?br /> 量q回接口而非实际的类型,如返回List而非ArrayListQ这样如果以后需要将ArrayList换成LinkedListӞ客户端代码不用改变。这是针对抽象~程?/p>